优雅地组合两个列表的元素,使它们在某个属性值中是唯一的?

Hat*_*tch 3 java java-8

假设我有这个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 = ???;
Run Code Online (Sandbox Code Playgroud)

List<Foo> uniqueFoos我要添加的所有元素listA,并listB因此所有FooS ^具有唯一的ID.也就是说,如果已经有一个FoouniqueFoos具有特定ID不添加其他Foo具有相同ID但跳过它来代替.

当然有简单的旧迭代,但我认为应该有更优雅的东西(可能涉及流,但不是强制性的),但我无法弄清楚...

我可以想到一个好的解决方案,涉及equals()基本上覆盖方法return id == other.id;并使用Setdistinct().不幸的是我无法覆盖,equals()因为对象相等不能改变.

实现这一目标的清晰有效方法是什么?

Fed*_*ner 6

你可以这样做Collectors.toMap:

Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
    .collect(Collectors.toMap(
        Foo::getId,
        f -> f,
        (oldFoo, newFoo) -> oldFoo))
    .values();
Run Code Online (Sandbox Code Playgroud)

如果您需要List而不是a Collection,只需执行以下操作:

List<Foo> listUniqueFoos = new ArrayList<>(uniqueFoos);
Run Code Online (Sandbox Code Playgroud)

如果您还需要保留元素的遭遇顺序,则可以使用为Collectors.toMap接收到Supplier的映射接受a 的重载版本:

Collection<Foo> uniqueFoos = Stream.concat(listA.stream(), listB.stream())
    .collect(Collectors.toMap(
        Foo::getId,
        f -> f,
        (oldFoo, newFoo) -> oldFoo,
        LinkedHashMap::new))
    .values();
Run Code Online (Sandbox Code Playgroud)

我认为值得添加一个非流变体:

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();
Run Code Online (Sandbox Code Playgroud)

这可以重构为通用方法,不重复代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

你可以使用如下:

Collection<Foo> uniqueFoos = uniqueBy(Foo::getId, listA, listB);
Run Code Online (Sandbox Code Playgroud)

这种方法使用该Map.merge方法.