Java Streams - Get a "symmetric difference list" from two other lists
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 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.
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();
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.
The 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.
Something like this may work:
Stream.concat(bigCarList.stream(), smallCarList.stream())
.collect(groupingBy(Function.identity(), counting()))
.entrySet().stream()
.filter(e -> e.getValue().equals(1L))
.map(Map.Entry::getKey)
.collect(toList());
Here we first collect all the cars to the Map<Car, Long>
where value is the number of such cars encountered. After that, we filter
this Map
leaving only cars that are encountered exactly once, drop the counts and collect to the final List
.