AssertEquals 2 Lists ignore order

JavaArraysUnit TestingJunitAssertion

Java Problem Overview


That should be really simple question I believe. But somehow I can't find answer in Google.

Assume that I have 2 Lists of Strings. First contains "String A" and "String B", second one contains "String B" and "String A" (notice difference in order). I want to test them with JUnit to check whether they contains exactly the same Strings.

Is there any assert that checks equality of Strings that ignore order? For given example org.junit.Assert.assertEquals throws AssertionError

java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>

Work around is to sort Lists firstly and then pass them to assertion. But I want my code to be as simple and clean as possible.

I use Hamcrest 1.3, JUnit 4.11, Mockito 1.9.5.

Java Solutions


Solution 1 - Java

As you mention that you use Hamcrest,
So I would pick one of the collection Matchers

import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertThat;

public class CompareListTest {

    @Test
    public void compareList() {
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");
        
        assertThat("List equality without order", 
            actual, containsInAnyOrder(expected.toArray()));
    }
    
}

Solution 2 - Java

You can use List.containsAll with JUnit's assertTrue to check that the first list contains every element from the second one, and vice versa.

assertEquals(expectedList.size(), actualList.size());
assertTrue(expectedList.containsAll(actualList));
assertTrue(actualList.containsAll(expectedList));

Hint:
This doesn't work with duplicates in the lists.

Solution 3 - Java

Here's a solution that avoids quadratic complexity (iterating over the lists multiple times). This uses the Apache Commons CollectionUtils class to create a Map of each item to a frequency count itself in the list. It then simply compares the two Maps.

Assert.assertEquals("Verify same metrics series",
    CollectionUtils.getCardinalityMap(expectedSeriesList),
    CollectionUtils.getCardinalityMap(actualSeriesList));

I also just spotted CollectionUtils.isEqualCollection that claims to do exactly what is being requested here...

https://commons.apache.org/proper/commons-collections/apidocs/index.html?org/apache/commons/collections4/CollectionUtils.html

Solution 4 - Java

With AssertJ, containsExactlyInAnyOrder() or containsExactlyInAnyOrderElementsOf() is what you need :

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

public class CompareListTest {

    @Test
    public void compareListWithTwoVariables() {
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrderElementsOf(expected);
    }

    @Test
    public void compareListWithInlineExpectedValues() {
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrder("String A", "String B");
    }    
}

Solution 5 - Java

    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

Solution 6 - Java

Im late to the party but here's my solution using Junit only. Any thoughts are welcome.

List<String> actual = new ArrayList<>();
actual.add("A");
actual.add("A");
actual.add("B");

List<String> expected = new ArrayList<>();
actual.add("A");
actual.add("B");
actual.add("B");

//Step 1: assert for size
assertEquals(actual.size(), expected.size());

//Step 2: Iterate
for(String e: expected){
    assertTrue(actual.contains(e));
    actual.remove(e);
}

Solution 7 - Java

For a quick fix I would check both ways:

assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));

And trying with a situation where the number of the same elements is different (e.g. 1, 1, 2 and 1, 2, 2) I didn't get false positives.

Solution 8 - Java

You can use ListAssert that comes in junit-addons jar.

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));

Solution 9 - Java

Note that solution by Roberto Izquierdo has quadratic complexity in general. Solution on HashSets always has linear complexity:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));

Solution 10 - Java

Looks like the other answers either reference 3rd party utils, are incorrect, or are inefficient.

Here's a O(N) vanilla solution in Java 8.

public static void assertContainsSame(Collection<?> expected, Collection<?> actual)
{
    assert expected.size() == actual.size();

    Map<Object, Long> counts = expected.stream()
        .collect(Collectors.groupingBy(
                item -> item,
                Collectors.counting()));

    for (Object item : actual)
        assert counts.merge(item, -1L, Long::sum) != -1L;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkukisView Question on Stackoverflow
Solution 1 - JavacheffeView Answer on Stackoverflow
Solution 2 - JavarobertoiaView Answer on Stackoverflow
Solution 3 - JavaAlex WordenView Answer on Stackoverflow
Solution 4 - JavadavidxxxView Answer on Stackoverflow
Solution 5 - JavaTinyfoolView Answer on Stackoverflow
Solution 6 - JavaBoss ManView Answer on Stackoverflow
Solution 7 - JavaKristjan VeskimäeView Answer on Stackoverflow
Solution 8 - JavarunakashView Answer on Stackoverflow
Solution 9 - JavaleventovView Answer on Stackoverflow
Solution 10 - JavaDaniel AveryView Answer on Stackoverflow