在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类内部的相等检查,是否可以简洁地做到这一点?
当前回答
不同的对象列表可以使用:
List distinctPersons = persons.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
ArrayList::new));
其他回答
另一个支持这个的库是jOOλ,它的Seq.distinct(Function<T,U>)方法:
Seq.seq(persons).distinct(Person::getName).toList();
实际上,它所做的事情与公认的答案几乎相同。
扩展Stuart Marks的回答,这可以用更短的方式完成,不需要并发映射(如果你不需要并行流):
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
final Set<Object> seen = new HashSet<>();
return t -> seen.add(keyExtractor.apply(t));
}
然后调用:
persons.stream().filter(distinctByKey(p -> p.getName());
我在这个清单中的解决方案:
List<HolderEntry> result ....
List<HolderEntry> dto3s = new ArrayList<>(result.stream().collect(toMap(
HolderEntry::getId,
holder -> holder, //or Function.identity() if you want
(holder1, holder2) -> holder1
)).values());
在我的情况下,我想找到不同的值,并把它们放在列表。
您可以将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();
}
}
另一种方法是将人名作为键放在地图中:
persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();
注意,如果名称重复,则保留的Person将是第一个遇到的Person。