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

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

persons.stream().distinct();

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

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

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


当前回答

有一种更简单的方法,使用带有自定义比较器的TreeSet。

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));

其他回答

你可以使用StreamEx库:

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()

另一种方法是将人名作为键放在地图中:

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

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

Set<YourPropertyType> set = new HashSet<>();
list
        .stream()
        .filter(it -> set.add(it.getYourProperty()))
        .forEach(it -> ...);

您可以将person对象包装到另一个类中,该类只比较person的名称。之后,您将打开被包装的对象以再次获得人员流。流操作可能如下所示:

persons.stream()
    .map(Wrapper::new)
    .distinct()
    .map(Wrapper::unwrap)
    ...;

类Wrapper可能看起来如下所示:

class Wrapper {
    private final Person person;
    public Wrapper(Person person) {
        this.person = person;
    }
    public Person unwrap() {
        return person;
    }
    public boolean equals(Object other) {
        if (other instanceof Wrapper) {
            return ((Wrapper) other).person.getName().equals(person.getName());
        } else {
            return false;
        }
    }
    public int hashCode() {
        return person.getName().hashCode();
    }
}

另一个支持这个的库是jOOλ,它的Seq.distinct(Function<T,U>)方法:

Seq.seq(persons).distinct(Person::getName).toList();

实际上,它所做的事情与公认的答案几乎相同。