Collections - Comparing two lists

Default behavior

Two lists are equal only if the size and the order matches. They have to contain the same elements in the same order.

List<String> list1 = Arrays.asList("1", "2", "3", "4");
List<String> list2 = Arrays.asList("1", "2", "3", "4");
List<String> list3 = Arrays.asList("1", "2", "4", "3");

@Test
public void whenTestingForEquality_ShouldBeEqual() throws Exception {
    Assert.assertEquals(list1, list2);
    Assert.assertNotSame(list1, list2);
    Assert.assertNotEquals(list1, list3);
}

In java, how do I compare two lists ignoring the order of elements in them?

We are looking to compare two lists in Java to see if they contain the same elements, regardless of their order. This is often called checking for “bag equality” or “multiset equality.”

Method 1: Convert to Frequencies (Using Maps)

This is generally the most robust and efficient method, especially if your lists can contain duplicate elements.

Concept: Count the occurrences of each element in both lists. If the frequency maps are identical, then the lists contain the same elements.

Steps:

  1. Create two HashMaps (or TreeMap if you need sorted keys, though it’s not strictly necessary for comparison).
  2. For each list, iterate through its elements and populate its respective map, incrementing the count for each element.
  3. Compare the two maps using equals(). HashMap.equals() correctly compares keys and their corresponding values.
    public static <T> boolean areListsEqualIgnoringOrder(List<T> list1, List<T> list2) {
            if (list1.size() != list2.size()) {
                return false; // Optimization: If sizes differ, they can't be equal
            }
    
            // Count frequencies for list1
            Map<T, Long> freqMap1 = list1.stream()
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    
            // Count frequencies for list2
            Map<T, Long> freqMap2 = list2.stream()
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
    
            return freqMap1.equals(freqMap2);
     }
    

Method 2: Sort and Compare

This is a simpler approach if the elements in your lists are comparable (implement Comparable or you provide a Comparator).

Concept: If two lists contain the same elements, then sorting both lists will result in identical sequences.

Steps:

  1. Create mutable copies of your input lists (as Collections.sort() sorts in-place).
  2. Sort both copies.
  3. Compare the sorted copies using equals()
    public static <T extends Comparable<T>> boolean areListsEqualIgnoringOrderSorted(List<T> list1, List<T> list2) {
            if (list1.size() != list2.size()) {
                return false;
            }
    
            List<T> sortedList1 = new ArrayList<>(list1);
            List<T> sortedList2 = new ArrayList<>(list2);
    
            Collections.sort(sortedList1);
            Collections.sort(sortedList2);
    
            return sortedList1.equals(sortedList2);
     }
    

Considerations for Sorting:

  1. Performance: Sorting takes O(N logN) time. For very large lists, the frequency map method (which is closer to O(N) on average) might be faster.
  2. Comparability: Elements must be comparable. If they aren’t, you’ll need to provide a custom Comparator.
  3. Mutable Copies: Always work with copies to avoid modifying the original lists.

Method 3: Convert to Sets (Only if no duplicates are allowed or significant)

Concept: If you’re certain that duplicates don’t matter or won’t be present, you can convert both lists to Sets and then compare the sets.

Steps:

  1. Convert both lists to HashSets.
  2. Compare the two sets using equals().
    public static <T> boolean areListsEqualIgnoringOrderAndDuplicates(List<T> list1, List<T> list2) {
            // This check is important as Set conversion will lose duplicate count
            if (list1.size() != list2.size()) {
                // If the sizes are different, and you are trying to equate lists where duplicates matter
                // (e.g. [1,1,2] != [1,2]), then this short-circuit is crucial.
                // If duplicates truly don't matter, then this check might be misleading.
                // For true "ignore order AND duplicates" equality, the size check is not strictly needed after set conversion.
                // However, most often, when "ignoring order", people still care about duplicates.
                // For strict "same elements present, regardless of count or order", remove this size check.
                // For the scenario where [1,1,2] is NOT equal to [1,2], keep it.
                 // But if [1,1,2] *is* considered equal to [1,2] (because elements 1 and 2 are present),
                 // then this method is still valid for that interpretation.
                // If you care about element count, use the frequency map or sort method.
            }
    
            Set<T> set1 = new HashSet<>(list1);
            Set<T> set2 = new HashSet<>(list2);
    
            return set1.equals(set2);
     }
    

Important Caveat for Set Method:

This method is only suitable if the presence of duplicate elements doesn’t matter for your definition of “equality.” If [1, 2, 2] should not be considered equal to [1, 2], then the Set approach is not appropriate.

If you remove the initial size check in areListsEqualIgnoringOrderAndDuplicates, it means you are truly ignoring duplicates. If you keep the size check, you’re implicitly saying that the number of distinct elements must be the same, and the set comparison ensures those distinct elements are the same.


Links to this note