1. 概述

在开发中,我们经常需要从列表中筛选出不同的元素。Java 8 引入的 Stream API 为数据处理提供了函数式编程的新途径。

本文将展示如何基于对象特定属性对集合进行去重,涵盖标准 API 及第三方库的多种实现方案。

2. 使用 Stream API

Stream API 提供了 distinct() 方法,它基于对象的 equals() 方法实现去重。但当我们需要按特定属性去重时,这个方法就显得不够灵活了。

2.1. 使用有状态过滤器

一种解决方案是实现一个有状态的 Predicate

public static <T> Predicate<T> distinctByKey(
    Function<? super T, ?> keyExtractor) {
  
    Map<Object, Boolean> seen = new ConcurrentHashMap<>(); 
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; 
}

测试时使用以下 Person 类(包含 age、email 和 name 属性):

public class Person { 
    private int age; 
    private String name; 
    private String email; 
    // 标准 getter/setter 
}

按 name 属性去重的示例:

List<Person> personListFiltered = personList.stream() 
  .filter(distinctByKey(p -> p.getName())) 
  .collect(Collectors.toList());

✅ 这种方式无需引入额外依赖
⚠️ 但在并行流中需注意线程安全(已使用 ConcurrentHashMap 解决)

3. 使用 Eclipse Collections

Eclipse Collections 是一个为 Java 提供高级集合处理能力的库。

3.1. 使用 ListIterate.distinct()

ListIterate.distinct() 方法支持通过 HashingStrategies 按特定属性去重:

按 name 去重:

List<Person> personListFiltered = ListIterate
  .distinct(personList, HashingStrategies.fromFunction(Person::getName));

对基本类型属性(如 int)有专门优化:

List<Person> personListFiltered = ListIterate.distinct(
  personList, HashingStrategies.fromIntFunction(Person::getAge));

3.2. Maven 依赖

在 pom.xml 中添加:

<dependency> 
    <groupId>org.eclipse.collections</groupId> 
    <artifactId>eclipse-collections</artifactId> 
    <version>8.2.0</version> 
</dependency>

最新版本可在 Maven Central 查找。

4. 使用 Vavr (Javaslang)

这是一个为 Java 8 提供函数式数据结构的库。

4.1. 使用 List.distinctBy

Vavr 的 List 类提供 distinctBy() 方法支持按属性去重:

List<Person> personListFiltered = List.ofAll(personList)
  .distinctBy(Person::getName)
  .toJavaList();

4.2. Maven 依赖

在 pom.xml 中添加:

<dependency> 
    <groupId>io.vavr</groupId> 
    <artifactId>vavr</artifactId> 
    <version>0.9.0</version>  
</dependency>

最新版本可在 Maven Central 查找。

5. 使用 StreamEx

这个库扩展了 Java Stream API 的功能。

5.1. 使用 StreamEx.distinct

StreamEx 类提供了直接按属性去重的 distinct 方法:

List<Person> personListFiltered = StreamEx.of(personList)
  .distinct(Person::getName)
  .toList();

5.2. Maven 依赖

在 pom.xml 中添加:

<dependency> 
    <groupId>one.util</groupId> 
    <artifactId>streamex</artifactId> 
    <version>0.6.5</version> 
</dependency>

最新版本可在 Maven Central 查找。

6. 总结

本文演示了在 Java 中按对象属性去重的多种方案:

  • 标准 Stream API:适合简单场景,无需额外依赖
  • Eclipse Collections:提供丰富的哈希策略,适合复杂场景
  • Vavr:函数式编程风格,适合链式操作
  • StreamEx:API 最简洁,直接支持属性去重

完整代码示例可在 GitHub 获取。根据项目需求选择合适方案,避免重复造轮子。


原始标题:DistinctBy in Java Stream API