Java?Stream去重常見(jiàn)的多種方法及示例
在Java Stream中實(shí)現(xiàn)去重有多種方法,具體取決于需求和場(chǎng)景。以下是常見(jiàn)的幾種方法及示例:
1. 使用 distinct() 方法
適用于對(duì)象已正確實(shí)現(xiàn) equals() 和 hashCode(),基于對(duì)象整體去重并保留順序:
List<Person> uniquePersons = persons.stream()
.distinct()
.collect(Collectors.toList());
2. 根據(jù)對(duì)象的屬性去重
方法一:使用 Collectors.toMap
根據(jù)屬性作為鍵,保留第一個(gè)或最后一個(gè)元素,支持順序(使用 LinkedHashMap):
// 保留第一個(gè)出現(xiàn)的元素
List<Person> uniqueByName = persons.stream()
.collect(Collectors.toMap(
Person::getName,
Function.identity(),
(oldP, newP) -> oldP, // 保留舊值(第一個(gè))
LinkedHashMap::new // 保持插入順序
))
.values().stream()
.collect(Collectors.toList());
// 保留最后一個(gè)出現(xiàn)的元素
List<Person> uniqueByNameLast = persons.stream()
.collect(Collectors.toMap(
Person::getName,
Function.identity(),
(oldP, newP) -> newP // 保留新值(最后一個(gè))
))
.values().stream()
.collect(Collectors.toList());
方法二:使用 filter 和線程安全的 Set
適用于并行流,但可能不保留順序:
// 并行流去重(不保證順序)
Set<String> seen = ConcurrentHashMap.newKeySet();
List<Person> uniqueByName = persons.parallelStream()
.filter(p -> seen.add(p.getName()))
.collect(Collectors.toList());
// 順序流去重(保留順序)
Set<String> seenOrdered = new HashSet<>();
List<Person> uniqueByNameOrdered = persons.stream()
.filter(p -> seenOrdered.add(p.getName()))
.collect(Collectors.toList());
方法三:使用 groupingBy
分組后取每組的第一個(gè)元素,保持順序:
List<Person> uniqueByName = persons.stream()
.collect(Collectors.groupingBy(
Person::getName,
LinkedHashMap::new, // 保持插入順序
Collectors.toList()
))
.values().stream()
.map(group -> group.get(0)) // 取第一個(gè)元素
.collect(Collectors.toList());
3. 根據(jù)字符串長(zhǎng)度去重示例
List<String> words = Arrays.asList("apple", "banana", "orange", "grape", "kiwi");
List<String> uniqueByLength = words.stream()
.collect(Collectors.toMap(
String::length,
Function.identity(),
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
))
.values().stream()
.collect(Collectors.toList());
// 結(jié)果: ["apple", "banana", "kiwi"](保留順序)
4. 自定義去重借助Filter 實(shí)現(xiàn):
自定義一個(gè) Predicate 函數(shù),用一個(gè) Set 來(lái)記錄已經(jīng)出現(xiàn)過(guò)的元素,然后過(guò)濾掉重復(fù)的元素。
//定義一個(gè)Predicate函數(shù)
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> sets = ConcurrentHashMap.newKeySet();
return t -> sets.add(keyExtractor.apply(t));
}
//根據(jù)age屬性去重
list.stream().filter(distinctByKey(s -> s.getAge()))
.forEach(System.out::println);
附:java中使用stream流根據(jù)對(duì)象中的某一字段進(jìn)行去重
在開(kāi)發(fā)中經(jīng)常會(huì)遇到數(shù)據(jù)去重的,單個(gè)基本類型的集合去重比較容易,比如String、Integer,直接使用流中的distinct方法去重即可。但是遇到對(duì)象集合,需要使用對(duì)象中的某個(gè)字段去重就不能使用這個(gè)方法了??梢栽诹魇骄幊讨屑尤隩reeSet,TreeSet是一個(gè)有序且不重復(fù)的有序集合。以用戶User數(shù)據(jù)位列:
List<User> list = userService.list();
list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing( f -> f.getDeptName()+":"+f.getStatus()))),ArrayList::new));這里是根據(jù)用戶的部門以及用戶的狀態(tài)進(jìn)行數(shù)據(jù)去重,將用戶的部門以及狀態(tài)拼接位一個(gè)字符串進(jìn)行去重,這樣stream內(nèi)部會(huì)將整個(gè)user集合數(shù)據(jù)組裝成這樣進(jìn)行整體去重,這樣去重下來(lái)的數(shù)據(jù)就是,每一個(gè)部門中只會(huì)存在一個(gè)狀態(tài)的部門人員數(shù)據(jù),這里是舉個(gè)例子,在實(shí)際環(huán)境中,你可以將User換成自己想要去重的實(shí)體集合。
總結(jié)
distinct():簡(jiǎn)單高效,適用于對(duì)象整體去重。toMap或groupingBy:靈活,支持按屬性去重,可控制保留順序。filter+Set:適合并行流,但需注意線程安全和順序問(wèn)題。
根據(jù)具體場(chǎng)景選擇最合適的方法,確保代碼簡(jiǎn)潔且性能良好。
到此這篇關(guān)于Java Stream去重常見(jiàn)的多種方法及示例的文章就介紹到這了,更多相關(guān)Java Stream去重方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot基于過(guò)濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作
這篇文章主要介紹了springboot基于過(guò)濾器實(shí)現(xiàn)接口請(qǐng)求耗時(shí)統(tǒng)計(jì)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
Java加權(quán)負(fù)載均衡策略實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Java加權(quán)負(fù)載均衡策略實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
SpringMVC框架實(shí)現(xiàn)上傳圖片的示例代碼
本篇文章主要介紹了SpringMVC框架實(shí)現(xiàn)上傳圖片的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
深度源碼解析Java 線程池的實(shí)現(xiàn)原理
如何高效的使用這些資源就是程序員在平時(shí)寫代碼時(shí)候的一個(gè)努力的方向。本文要說(shuō)的線程池就是一種對(duì) CPU 利用的優(yōu)化手段。對(duì)Java 線程池的實(shí)現(xiàn)原理相關(guān)知識(shí)感興趣的朋友一起看看吧2021-05-05
Java通過(guò)模板替換實(shí)現(xiàn)excel的傳參填寫方式
文章介紹了使用Java模板替換功能來(lái)實(shí)現(xiàn)Excel文件的傳參填寫,通過(guò)將模板中的占位符(如$)進(jìn)行轉(zhuǎn)義和替換,可以動(dòng)態(tài)生成包含具體數(shù)據(jù)的Excel文件,這種方法適用于需要批量生成帶有個(gè)性化數(shù)據(jù)的Excel文檔的場(chǎng)景2025-12-12
Spring線程池的配置及使用ThreadPoolTaskExecutor過(guò)程
本文主要介紹了Spring提供的線程池ThreadPoolTaskExecutor及其配置方式,以及FixedThreadPool線程池的使用注意事項(xiàng),同時(shí)強(qiáng)調(diào)了計(jì)數(shù)器的使用可以保證線程池執(zhí)行順序2026-03-03
Java實(shí)現(xiàn)String轉(zhuǎn)Integer的四種方案對(duì)比
在Java項(xiàng)目開(kāi)發(fā)中,經(jīng)常會(huì)有類型轉(zhuǎn)換處理,這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)String轉(zhuǎn)Integer的四種方案對(duì)比,感興趣的小伙伴可以了解下2026-03-03

