-2

If I had, say separate 3 nested ArrayLists of Strings, i.e., ArrayList<ArrayList<String>>:

  1. What is the most efficient way of finding their intersection (common elements)?
  2. Are there other data structures to replace the nested ArrayLists structure that could improve the efficiency of finding their intersection? (e.g. The first structure I can think of is using a Set, but I would like to see if there are other suggestions for this.)

Thanks in advance!

  • 2
    Yep, you want a set. Any kind of set will do. `HashSet` will probably be faster in general, whereas `TreeSet` will behave more predictably and may be faster at small sizes – Silvio Mayolo May 15 '21 at 23:41

2 Answers2

1

i would use the list.retainAll method as in

private ArrayList<String> getIntersection(ArrayList<ArrayList<String>> lists) {
    if(null == lists || lists.isEmpty()) {
        return null;
    }
    ArrayList<String> intersection = lists.get(0);
    lists.forEach(intersection::retainAll);
    return intersection;
}
tremendous7
  • 721
  • 6
  • 9
1

The intersection of two lists is done using the retainAll() method.

It update the list, so if you don't want that, you should copy the list first.

If you have more than 2 lists, you copy the first and then call retainAll() for each of the remaining lists.

ArrayList<ArrayList<String>> lists = ...

List<String> intersection = new ArrayList<>(lists.get(0));
for (List<String> list : lists.subList(1, lists.size()))
    intersection.retainAll(list);

However performance will be a bad O(n*m), where n and m are the sizes of the two largest lists.

That is because retainAll() does a contains() against the list given in the parameter, which is a sequential search, for each element in the intersection list.

Performance can be improved to O(n), where n is the largest list, by converting the lists to sets.

List<String> intersection = new ArrayList<>(lists.get(0));
for (List<String> list : lists.subList(1, lists.size()))
    intersection.retainAll(new HashSet<>(list));

In Java 8+, the for loop can be simplified to one of these:

lists.subList(1, lists.size()).stream().map(HashSet::new).forEach(intersection::retainAll);

lists.subList(1, lists.size()).forEach(list -> intersection.retainAll(new HashSet<>(list)));
Andreas
  • 154,647
  • 11
  • 152
  • 247