在Java 8中,我如何使用流API通过检查每个对象的属性的清晰度来过滤一个集合?

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

persons.stream().distinct();

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

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

不幸的是,distinct()方法没有这样的重载。如果不修改Person类内部的相等检查,是否可以简洁地做到这一点?


当前回答

不同的对象列表可以使用:

 List distinctPersons = persons.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                            ArrayList::new));

其他回答

实现这一点最简单的方法是跳到sort特性上,因为它已经提供了一个可选的Comparator,可以使用元素的属性创建。然后你必须过滤掉重复项,这可以使用一个状态完备的Predicate来完成,它使用的事实是,对于一个已排序的流,所有相等的元素是相邻的:

Comparator<Person> c=Comparator.comparing(Person::getName);
stream.sorted(c).filter(new Predicate<Person>() {
    Person previous;
    public boolean test(Person p) {
      if(previous!=null && c.compare(previous, p)==0)
        return false;
      previous=p;
      return true;
    }
})./* more stream operations here */;

当然,一个有状态的Predicate不是线程安全的,但是如果你需要,你可以把这个逻辑移到一个Collector中,让流在使用你的Collector时处理线程安全。这取决于你想如何处理你在问题中没有告诉我们的不同元素流。

我们也可以使用RxJava(非常强大的响应式扩展库)

Observable.from(persons).distinct(Person::getName)

or

Observable.from(persons).distinct(p -> p.getName())

如果你想要名单,下面是最简单的方法

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

此外,如果您想要查找不同的或唯一的名称列表,而不是Person,您也可以使用以下两个方法。

方法一:使用区别

persons.stream().map(x->x.getName()).distinct.collect(Collectors.toList());

方法二:使用HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));

如果可以的话,我推荐使用Vavr。有了这个库,你可以做以下事情:

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection

我的方法是将所有具有相同属性的对象分组在一起,然后将组缩短为1,最后将它们收集为List。

  List<YourPersonClass> listWithDistinctPersons =   persons.stream()
            //operators to remove duplicates based on person name
            .collect(Collectors.groupingBy(p -> p.getName()))
            .values()
            .stream()
            //cut short the groups to size of 1
            .flatMap(group -> group.stream().limit(1))
            //collect distinct users as list
            .collect(Collectors.toList());