Java Streams - 获取“对称差异列表";来自另外两个列表
我正在尝试使用 Java 8 流来组合列表.如何从两个现有列表中获取对称差异列表"(仅存在于一个列表中的所有对象).我知道如何获取相交列表以及如何获取联合列表.
Im trying to use Java 8 streams to combine lists. How can I get a "symmetric difference list" (all object that only exist in one list) from two existing lists. I know how to get an intersect list and also how to get a union list.
在下面的代码中,我想要两个汽车列表(bigCarList、smallCarList)中不相交的汽车.我希望结果是包含 2 辆车(Toyota Corolla"和Ford Focus")的列表
In the code below I want the disjoint Cars from the two lists of cars (bigCarList,smallCarList). I expect the result to be a list with the 2 cars ("Toyota Corolla" and "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());
只需过滤一个列表中未包含在另一个列表中的所有项目,反之亦然,然后连接两个结果.这对于小型列表非常有效,在考虑优化解决方案(如散列或生成结果 distinct()
之前,您应该问自己为什么要使用列表,如果您既不想要,也不想要重复或特定顺序).
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.
看起来您实际上想要的是 Set
,而不是 List
.如果你使用 Set
s,Tagir Valeev 的解决方案 是合适的.但它不适用于 List
s 的实际语义,即如果源列表包含重复项则不起作用.
It seems like you actually want Set
s, not List
s. If you use Set
s, Tagir Valeev’s solution is appropriate. But it is not working with the actual semantics of List
s, i.e. doesn’t work if the source lists contain duplicates.
但是如果你使用Set
s,代码可以更简单:
But if you are using Set
s, 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();
这使用了 toMap
收集器,它创建了一个 Map
(值无关紧要,我们在这里简单地映射到 true
)并使用合并处理重复项的功能.由于对于两个集合,只有当两个集合中都包含一个项目时才会发生重复,因此这些是我们要删除的项目.
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. Since for two sets, duplicates can only occur when an item is contained in both sets, these are the items we want remove.
Collectors.toMap
的文档说合并函数被视为提供给 Map.merge(Object, Object, BiFunction)
",我们可以从那里学到,简单地将重复的对映射到 null
将删除条目.
所以之后,地图的keySet()
包含了不相交集.
So afterwards, the keySet()
of the map contains the disjoint set.
相关文章