假设我有这个Java 8代码:
public class Foo {
private long id;
public getId() {
return id;
}
//--snip--
}
//Somewhere else...
List<Foo> listA = getListA();
List<Foo> listB = getListB();
List<Foo> uniqueFoos = ???;
在List<Foo> uniqueFoos
我想添加listA
和listB
的所有元素,以便所有Foo
都有唯一的ID。即如果Foo
中已有uniqueFoos
具有特定ID,则不要添加具有相同ID的其他Foo
,而是跳过它。
当然有一个简单的旧迭代,但我认为应该有更优雅的东西(可能涉及流,但不是强制性的),但我无法弄明白......
我可以想出一个好的解决方案,涉及覆盖equals()
方法基本上return id == other.id;
并使用Set
或distinct()
。不幸的是,我无法覆盖equals()
因为对象相等不能改变。
实现这一目标的有效方法是什么?
答案 0 :(得分:6)
您可以使用Collectors.toMap
:
Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
.collect(Collectors.toMap(
Foo::getId,
f -> f,
(oldFoo, newFoo) -> oldFoo))
.values();
如果您需要List
而不是Collection
,请执行以下操作:
List<Foo> listUniqueFoos = new ArrayList<>(uniqueFoos);
如果您还需要保留元素的遭遇顺序,则可以使用接受Supplier
的重载版Collectors.toMap
作为返回的地图:
Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
.collect(Collectors.toMap(
Foo::getId,
f -> f,
(oldFoo, newFoo) -> oldFoo,
LinkedHashMap::new))
.values();
我认为值得添加非流式变体:
Map<Long, Foo> map = new LinkedHashMap<>();
listA.forEach(f -> map.merge(g.getId(), f, (oldFoo, newFoo) -> oldFoo));
listB.forEach(f -> map.merge(g.getId(), f, (oldFoo, newFoo) -> oldFoo));
Collection<Foo> uniqueFoos = map.values();
这可以重构为通用方法,不重复代码:
static <T, K> Collection<T> uniqueBy(Function<T, K> groupBy, List<T>... lists) {
Map<K, T> map = new LinkedHashMap<>();
for (List<T> l : lists) {
l.forEach(e -> map.merge(groupBy.apply(e), e, (o, n) -> o));
}
return map.values();
}
您可以使用以下内容:
Collection<Foo> uniqueFoos = uniqueBy(Foo::getId, listA, listB);
此方法使用Map.merge
方法。
答案 1 :(得分:2)
这样的事情会做。
List<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
.filter(distinctByKey(Foo::getId))
.collect(Collectors.toList());
public <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
答案 2 :(得分:1)
你可以写这个。由于filter()
和使用存储遇到的ID的Set,跳过具有相同id的第二个和下一个元素:
Set<Long> ids = new HashSet<>();
List<Foo> uniqueFoos = Stream.concat(getListA().stream(), getListB().stream())
.filter(f -> ids.add(f.getId()))
.collect(Collectors.toList());
它不是一个完整的流解决方案,但它是相当直接和可读的。