Java ProgramsCollectionsFind Common Elements in Lists

Find Common Elements in Lists in Java

beginner·  Collections  ·  Conversions

Problem

Finding the common elements between two Lists means keeping only the values that appear in both — Java's retainAll() removes everything from one list that isn't also present in another.

Given two Lists, find the elements that appear in both.

Input
list1 = [1, 2, 3, 4, 5], list2 = [3, 4, 5, 6, 7]
Output
[3, 4, 5]

Java Program

Java
import java.util.ArrayList; import java.util.List; public class FindCommonElementsInLists { public static void main(String[] args) { List<Integer> list1 = new ArrayList<>(List.of(1, 2, 3, 4, 5)); List<Integer> list2 = new ArrayList<>(List.of(3, 4, 5, 6, 7)); List<Integer> common = new ArrayList<>(list1); // copy — list1 itself stays untouched common.retainAll(list2); // keeps only elements also present in list2 System.out.println(common); } }

Output

[3, 4, 5]

Core Logic

Copying the first list, then calling retainAll() with the second, strips out every element the copy doesn't share with the second list, leaving only the intersection.

How It Works
  1. 1new ArrayList<>(list1) makes a copy of list1, so the original isn't modified by what comes next.
  2. 2common.retainAll(list2) removes every element from common that isn't also present in list2, keeping only the shared values.
  3. 3list1 and list2 both stay exactly as they were — only the separate common list changes.
  4. 4What's left in common is exactly the elements present in both original lists.
For list1 = [1, 2, 3, 4, 5] and list2 = [3, 4, 5, 6, 7], retainAll() strips 1 and 2 from the copy, leaving [3, 4, 5].
💡

Key Point: Calling retainAll() directly on list1 would mutate it in place — copying first keeps both original lists intact, which is usually what you want unless you specifically mean to filter one of them permanently.

Complexity
Time Complexity: O(n × m)Space Complexity: O(n)

Why: For each of the n elements in the copy, retainAll() checks whether it's present in list2 via contains(), which costs O(m) on a plain List — converting list2 to a HashSet first would make each check O(1) on average, bringing the whole operation down to O(n + m).

Key Concepts

ArrayListretainAll()list intersection

Approach 2: Java 8

Java
import java.util.List; import java.util.stream.Collectors; public class FindCommonElementsInListsStream { public static void main(String[] args) { List<Integer> list1 = List.of(1, 2, 3, 4, 5); List<Integer> list2 = List.of(3, 4, 5, 6, 7); // Keeps only the elements from list1 that list2 also contains List<Integer> common = list1.stream() .filter(list2::contains) .collect(Collectors.toList()); System.out.println(common); } }

Output

[3, 4, 5]

Core Logic

Filtering list1's stream down to only the elements list2 also contains reproduces the same intersection as a pipeline, without a separate copy-and-retainAll step.

How It Works
  1. 1list1.stream() opens a stream over the first list's elements.
  2. 2.filter(list2::contains) keeps only the elements that are also present in list2, using a method reference in place of a lambda.
  3. 3.collect(Collectors.toList()) gathers the surviving elements into a new List.
  4. 4Neither original list is modified — list1 is only read from, and common is a completely new List.
For list1 = [1, 2, 3, 4, 5] and list2 = [3, 4, 5, 6, 7], filtering keeps only 3, 4, and 5, producing [3, 4, 5].
💡

Key Point: list2::contains is a method reference passed directly as the filter predicate — it reads as 'keep elements list2 contains', with no separate retainAll() call or intermediate copy needed.

Complexity
Time Complexity: O(n × m)Space Complexity: O(n)

Why: filter() still calls contains() once per element of list1, each costing O(m) on a plain List — the same asymptotic cost as retainAll(), just expressed as a pipeline instead of a mutating call.

Key Concepts

Streamfilter()Collectors.toList()

Related Programs