Java 8独特的财产



Answers

另一种方法是将人员作为关键字放置在地图中:

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

请注意,如果名称重复,则保留的人员将成为第一名。

Question

在Java 8中,如何通过检查每个对象的属性的独特性来使用Stream 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())) 
));



您可以使用groupingBy收集器:

persons.collect(groupingBy(p -> p.getName())).values().forEach(t -> System.out.println(t.get(0).getId()));

如果你想有另一个流,你可以使用这个:

persons.collect(groupingBy(p -> p.getName())).values().stream().map(l -> (l.get(0)));



您可以在Eclipse集合中使用distinct(HashingStrategy)方法。

List<Person> persons = ...;
MutableList<Person> distinct =
    ListIterate.distinct(persons, HashingStrategies.fromFunction(Person::getName));

如果您可以重构persons以实现Eclipse Collections界面,则可以直接在列表中调用方法。

MutableList<Person> persons = ...;
MutableList<Person> distinct =
    persons.distinct(HashingStrategies.fromFunction(Person::getName));

HashingStrategy只是一个策略接口,允许您定义equals和hashcode的自定义实现。

public interface HashingStrategy<E>
{
    int computeHashCode(E object);
    boolean equals(E object1, E object2);
}

注意:我是Eclipse集合的提交者。




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

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



实现这个最简单的方法是跳转排序功能,因为它已经提供了一个可选的Comparator ,它可以使用元素的属性创建。 然后你必须使用statefull Predicate来完成可以完成的重复项,这个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时保证线程安全。 这取决于你想对你在问题中没有告诉我们的不同元素流做什么。




您可以编写的最简单的代码:

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



您可以使用StreamEx库:

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



Links