Java Streams - 从其他两个列表中获取 "symmetric difference list"

标签 java list java-8 java-stream

我正在尝试使用 Java 8 流来组合列表。 如何从两个现有列表中获取“对称差异列表”(所有对象仅存在于一个列表中)。 我知道如何获得相交列表以及如何获得联合列表。

在下面的代码中,我想要来自两个汽车列表(bigCarList、smallCarList)的不相交的汽车。 我希望结果是包含 2 辆汽车(“Toyota Corolla”和“Ford Focus”)的列表

示例代码:

public void testDisjointLists() {
    List<Car> bigCarList = get5DefaultCars();
    List<Car> smallCarList = get3DefaultCars();

    //Get cars that exists in both lists
    List<Car> intersect = bigCarList.stream().filter(smallCarList::contains).collect(Collectors.toList());

    //Get all cars in both list as one list
    List<Car> union = Stream.concat(bigCarList.stream(), smallCarList.stream()).distinct().collect(Collectors.toList());

    //Get all cars that only exist in one list
    //List<Car> disjoint = ???

}

public List<Car> get5DefaultCars() {
    List<Car> cars = get3DefaultCars();
    cars.add(new Car("Toyota Corolla", 2008));
    cars.add(new Car("Ford Focus", 2010));
    return cars;
}

public List<Car> get3DefaultCars() {
    List<Car> cars = new ArrayList<>();
    cars.add(new Car("Volvo V70", 1990));
    cars.add(new Car("BMW I3", 1999));
    cars.add(new Car("Audi A3", 2005));
    return cars;
}

class Car {
    private int releaseYear;
    private String name;
    public Car(String name) {
        this.name = name;
    }
    public Car(String name, int releaseYear) {
        this.name = name;
        this.releaseYear = releaseYear;
    }

    //Overridden equals() and hashCode()
}

最佳答案

Based on your own code, there is a straight-forward solution:

List<Car> disjoint = Stream.concat(
    bigCarList.stream().filter(c->!smallCarList.contains(c)),
    smallCarList.stream().filter(c->!bigCarList.contains(c))
).collect(Collectors.toList());

Just filter one list for all items not contained in the other and vice versa and concatenate both results. That works fairly well for small lists and before consider optimized solutions like hashing or making the result distinct() you should ask yourself why you are using lists if you don’t want neither, duplicates nor a specific order.

It seems like you actually want Sets, not Lists. If you use Sets, Tagir Valeev’s solution is appropriate. But it is not working with the actual semantics of Lists, i.e. doesn’t work if the source lists contain duplicates.


But if you are using Sets, the code can be even simpler:

Set<Car> disjoint = Stream.concat(bigCarSet.stream(), smallCarSet.stream())
  .collect(Collectors.toMap(Function.identity(), t->true, (a,b)->null))
  .keySet();

This uses the toMap collector which creates a Map (the value is irrelevant, we simply map to true here) and uses a merge function to handle duplicates.由于对于两个集合,只有当两个集合中都包含一个项目时才会出现重复项,因此这些是我们要删除的项目。

documentation of Collectors.toMap says that the merge function is treated “as supplied to Map.merge(Object, Object, BiFunction) ” and we can learn from there, that simply mapping the duplicate pair to null will remove the entry.

So afterwards, the keySet() of the map contains the disjoint set.

关于Java Streams - 从其他两个列表中获取 "symmetric difference list",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31074510/

相关文章:

java - 为什么模拟器在假调用后关闭

python - 过滤复杂列表中的元素

python - 如何将前导数字添加到空列表中并用输入填充它

python 按值而不是按引用列出

java - 如何使用 javaParser 在 java 文件中搜索?

Java 8 条件过滤器 | lambda 斯

java - 为另一个构造函数提供类型变量值

Java 8 : Converting an array of strings to have first character in uppercase and sorted order

java - 如何使用流和 lambda 转换包含简单 for 循环的 java 代码?

Java 8 lambda Collectors.groupingBy 与 Collector.toList() 中的 map