Java Stream去重实践

175 阅读1分钟

Java Stream中的distinct()方法

distinct()方法是Stream API中的一个中间操作,它返回一个去除了重复元素的新Stream。默认情况下,它使用对象的equals()方法来判断元素是否相等。但是,如果我们需要按照对象的特定字段来进行去重,就需要结合使用distinct()方法和自定义比较器。

1.1 根据单个字段进行去重

假设我们有一个Person类,其中包含idname两个字段。我们想要按照id字段对Person对象进行去重,可以按照以下步骤操作,distinctByKey方法,它接受一个函数式参数keyExtractor,返回一个Predicate<T>。Predicate<T> 用于去重判断,即判断传入的元素是否与之前处理过的元素重复。 具体实现中,使用ConcurrentHashMap.newKeySet()创建了一个线程安全的集合seen,用于存储已经处理过的元素的键值。在每次判断时,通过keyExtractor.apply(t)获取当前元素的键值,并调用 seen.add(keyExtractor.apply(t))判断该键值是否已经存在。如果不存在,则返回true表示当前元素不重复;如果已经存在,则返回false表示当前元素重复。:

List<Person> people = // 获取Person对象的集合

List<Person> uniquePeople = people.stream()
                                  .filter(distinctByKey(Person::getId))
                                  .collect(Collectors.toList());

// 自定义去重比较器
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

1.2 根据多个字段进行去重

List<Person> uniquePeople = people.stream()
                                  .filter(distinctByKeys(p -> Arrays.asList(p.getId(), p.getName())))
                                  .collect(Collectors.toList());

// 自定义多字段去重比较器
public static <T> Predicate<T> distinctByKeys(Function<? super T, ? extends List<?>> keyExtractors) {
    Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
    return t -> {
        List<?> keys = keyExtractors.apply(t);
        return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
}