在Java 8中,我如何使用流API通过检查每个对象的属性的清晰度来过滤一个集合?
例如,我有一个Person对象列表,我想删除同名的人,
persons.stream().distinct();
将对Person对象使用默认的相等性检查,所以我需要这样的东西,
persons.stream().distinct(p -> p.getName());
不幸的是,distinct()方法没有这样的重载。如果不修改Person类内部的相等检查,是否可以简洁地做到这一点?
在Java 8中,我如何使用流API通过检查每个对象的属性的清晰度来过滤一个集合?
例如,我有一个Person对象列表,我想删除同名的人,
persons.stream().distinct();
将对Person对象使用默认的相等性检查,所以我需要这样的东西,
persons.stream().distinct(p -> p.getName());
不幸的是,distinct()方法没有这样的重载。如果不修改Person类内部的相等检查,是否可以简洁地做到这一点?
当前回答
你能写的最简单的代码:
persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());
其他回答
因为每个人都在分享他们自己的想法和实施方法,我也有一个,它不是一个有效的方法,但它是有效的:
Set<String> personNameList = personList.stream().
map(tempPerson->tempPerson.getName()).collect(Collectors.toSet());
personList.stream().
collect(()->new ArrayList<Person>(),
(l1,p)->{
if(!personNameList.contains(p.getName())) {
l1.add(p);
}
}, ArrayList::addAll);
将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()对任意键?)
我们也可以使用RxJava(非常强大的响应式扩展库)
Observable.from(persons).distinct(Person::getName)
or
Observable.from(persons).distinct(p -> p.getName())
不同的对象列表可以使用:
List distinctPersons = persons.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
ArrayList::new));
另一个解决方案,使用Set。也许不是理想的解决方案,但有效吗
Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());
或者如果可以修改原始列表,可以使用removeIf方法
persons.removeIf(p -> !set.add(p.getName()));