Java 8 按属性区分

在Java 8中,如何通过检查每个对象的属性的独特性来使用API过滤集合?Stream

例如,我有一个对象列表,我想删除具有相同名称的人,Person

persons.stream().distinct();

将对对象使用默认的相等性检查,所以我需要类似的东西,Person

persons.stream().distinct(p -> p.getName());

不幸的是,该方法没有这样的重载。在不修改类内部的相等性检查的情况下,是否可以简洁地执行此操作?distinct()Person


答案 1

考虑成为有状态筛选器。下面是一个函数,它返回一个谓词,该谓词维护有关以前看到的内容的状态,并返回给定元素是否是第一次看到的:distinct

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

然后你可以写:

persons.stream().filter(distinctByKey(Person::getName))

请注意,如果流是有序的并且并行运行,这将保留重复项中的任意元素,而不是像现在这样保留第一个元素。distinct()

(这与我对这个问题的回答基本相同:任意键上的Java Lambda Stream Distinct()?)


答案 2

另一种方法是使用姓名作为键将人员放置在地图上:

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

请注意,如果名称重复,则保留的将是第一个入的名称。Person