断言等于2列表忽略顺序

这应该是一个很简单的问题,但是我在谷歌上找不到答案。

假设我有两个字符串列表。第一个包含 “字符串 A”和“字符串 B”,第二个包含 “字符串 B”和“字符串 A”(注意顺序不同)。我想用 JUnit测试它们,以检查它们是否包含 一模一样字符串。

是否有任何断言检查忽略顺序的字符串的相等性

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

解决方法是首先对 List 进行排序,然后将它们传递给断言。但我希望我的代码尽可能简单和干净。

我用 Hamcrest 1.3JUnit 4.11Mockito 1.9.5

105644 次浏览

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.

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.

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)));

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()));
}
    

}

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

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

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

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);
}
    Collections.sort(excepted);
Collections.sort(actual);
assertEquals(excepted,actual);

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");
}
}

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;
}