Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spot the difference between two lists

In java suppose I have 2 lists

List<Object1> list1
List<Object2> list2

object1.getName(); returns a String
object2.getName(); return a String

is there any way to compare the names and get a difference of the two list

those 2 objects are defined in the 3rd party library, and I can't override the equals and compareto methods

I am in favour of googles Guava or commons collections library

but the Sets.symmetricDifference(Set1, Set2) ask for 2 to be passed in, even i juse Sets.newHashSet(lis1) and Sets.newHashSet(lis2) to create two sets but still they have difference type of objects in the sets.

or in commons CollectionUtils.disjunction(lis1, list2) the lists still has to contain the same object type

without doing 2 expensive for loops, is there any other way?

like image 309
Junchen Liu Avatar asked Oct 21 '22 02:10

Junchen Liu


1 Answers

First, we'll build two maps, one for each list, mapping names to objects. Then we iterate over the differences between the key sets, processing whichever kind of object had that name. The maps let us avoid scanning through the list looking for the object with that name. (In using Map rather than Multimap, I'm relying on the asker's comment on another answer that within each list, names are unique. If you're still using Java 7, replace the method reference with a Function implementation.)

Map<String, Object1> map1 = Maps.uniqueIndex(list1, Object1::getName);
Map<String, Object2> map2 = Maps.uniqueIndex(list2, Object1::getName);
for (String name : Sets.difference(map1.keySet(), map2.keySet()))
    processObject1(map1.get(name));
for (String name : Sets.difference(map2.keySet(), map1.keySet()))
    processObject2(map2.get(name));

If all you want to do is build lists or sets of the objects in exactly one list, processObject1 and processObject2 can just add the objects to collections.

uniqueIndex's iteration order is that of the input iterable, and difference returns a SetView with the same iteration order as its first argument, so you can process objects in the order they appeared in the input lists, if that order is relevant to your problem.


Java 8 streams provide basically the same functionality:

Map<String, Object1> map1 = list1.stream().collect(Collectors.toMap(Function.identity(), Object1::getName));
Map<String, Object2> map2 = list2.stream().collect(Collectors.toMap(Function.identity(), Object2::getName));
map1.keySet().stream().filter(n -> !map2.keySet().contains(n)).map(map1::get).forEachOrdered(o1 -> processObject1(o1));
map2.keySet().stream().filter(n -> !map1.keySet().contains(n)).map(map2::get).forEachOrdered(o2 -> processObject1(o2));

Again, you can replace the forEachOrdered call with collect(Collectors.toList()) if you just want to collect the objects.

like image 57
Jeffrey Bosboom Avatar answered Oct 24 '22 03:10

Jeffrey Bosboom