在Java Stream中实现去重有多种方法,具体取决于需求和场景。以下是常见的几种方法及示例:
1. 使用 distinct()
方法
适用于对象已正确实现 equals()
和 hashCode()
,基于对象整体去重并保留顺序:
java
List<Person> uniquePersons = persons.stream()
.distinct()
.collect(Collectors.toList());
2. 根据对象的属性去重
方法一:使用 Collectors.toMap
根据属性作为键,保留第一个或最后一个元素,支持顺序(使用 LinkedHashMap
):
java
// 保留第一个出现的元素
List<Person> uniqueByName = persons.stream()
.collect(Collectors.toMap(
Person::getName,
Function.identity(),
(oldP, newP) -> oldP, // 保留旧值(第一个)
LinkedHashMap::new // 保持插入顺序
))
.values().stream()
.collect(Collectors.toList());
// 保留最后一个出现的元素
List<Person> uniqueByNameLast = persons.stream()
.collect(Collectors.toMap(
Person::getName,
Function.identity(),
(oldP, newP) -> newP // 保留新值(最后一个)
))
.values().stream()
.collect(Collectors.toList());
方法二:使用 filter
和线程安全的 Set
适用于并行流,但可能不保留顺序:
java
// 并行流去重(不保证顺序)
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
分组后取每组的第一个元素,保持顺序:
java
List<Person> uniqueByName = persons.stream()
.collect(Collectors.groupingBy(
Person::getName,
LinkedHashMap::new, // 保持插入顺序
Collectors.toList()
))
.values().stream()
.map(group -> group.get(0)) // 取第一个元素
.collect(Collectors.toList());
3. 根据字符串长度去重示例
java
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());
// 结果: ["apple", "banana", "kiwi"](保留顺序)
4. 自定义去重借助Filter 实现:
自定义一个 Predicate 函数,用一个 Set 来记录已经出现过的元素,然后过滤掉重复的元素。
java
//定义一个Predicate函数
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> sets = ConcurrentHashMap.newKeySet();
return t -> sets.add(keyExtractor.apply(t));
}
//根据age属性去重
list.stream().filter(distinctByKey(s -> s.getAge()))
.forEach(System.out::println);
总结
distinct()
:简单高效,适用于对象整体去重。toMap
或groupingBy
:灵活,支持按属性去重,可控制保留顺序。filter
+Set
:适合并行流,但需注意线程安全和顺序问题。
根据具体场景选择最合适的方法,确保代码简洁且性能良好。