Java中如何比較兩個列表忽略的順序?

1.概述

有時在編寫單元測試時,我們需要對列表進行順序不可知的比較。在這個簡短的教程中,我們將研究如何編寫此類單元測試的不同示例。

2.設定

根據List#equals Java文檔,如果兩個列表包含相同順序的相同元素,則兩個列表相等。因此,我們不能只使用equals方法,而是要進行順序不可知比較。

在本教程中,我們將使用這三個列表作為測試的示例輸入:

List first = Arrays.asList(1, 3, 4, 6, 8);

 List second = Arrays.asList(8, 1, 6, 3, 4);

 List third = Arrays.asList(1, 3, 3, 6, 6);

有多種方法可用於順序不可知比較。讓我們一一看一下。

3.使用JUnit

JUnit是一個眾所周知的框架,用於Java生態系統中的單元測試。

assertTrueassertFalse方法使用以下邏輯比較兩個列表的相等性。

在這裡,我們檢查兩個列表的大小,並檢查第一個列表是否包含第二個列表的所有元素,反之亦然。儘管此解決方案有效,但可讀性不高。現在讓我們來看一些替代方案:

@Test

 public void whenTestingForOrderAgnosticEquality_ShouldBeTrue() {

 assertTrue(first.size() == second.size() && first.containsAll(second) && second.containsAll(first));

 }

在第一個測試中,比較兩個列表的大小,然後檢查兩個列表中的元素是否相同。當這兩個條件都返回true,我們的測試將通過。

現在讓我們看一下失敗的測試:

@Test

 public void whenTestingForOrderAgnosticEquality_ShouldBeFalse() {

 assertFalse(first.size() == third.size() && first.containsAll(third) && third.containsAll(first));

 }

相反,在此版本的測試中,儘管兩個列表的大小相同,但所有元素都不匹配。

4.使用AssertJ

AssertJ是一個開源社區驅動的庫,用於在Java測試中編寫流暢而豐富的斷言。

要在我們的maven項目中使用它,讓我們pom.xml文件中assertj-core依賴項:

<dependency>

 <groupId>org.assertj</groupId>

 <artifactId>assertj-core</artifactId>

 <version>3.16.1</version>

 </dependency>

讓我們編寫一個測試來比較相同元素和相同大小的兩個列表實例的相等性:

@Test

 void whenTestingForOrderAgnosticEqualityBothList_ShouldBeEqual() {

 assertThat(first).hasSameElementsAs(second);

 }

在此示例中,我們first驗證包含給定可迭代元素的所有元素,並且沒有其他任何順序。這種方法的主要局限性是hasSameElementsAs方法將忽略重複項。

讓我們在實踐中看一下這是什麼意思:

@Test

 void whenTestingForOrderAgnosticEqualityBothList_ShouldNotBeEqual() {

 List a = Arrays.asList("a", "a", "b", "c");

 List b = Arrays.asList("a", "b", "c");

 assertThat(a).hasSameElementsAs(b);

 }

在此測試中,儘管我們具有相同的元素,但是兩個列表的大小並不相等,但是斷言仍然是正確的,因為它忽略了重複項。為了使其正常工作,我們需要為兩個列表添加一個大小檢查:

assertThat(a).hasSize(b.size()).hasSameElementsAs(b);

確實添加了對我們兩個列表的大小的檢查,然後添加方法hasSameElementsAs確實會按預期失敗。

5.使用Hamcrest

如果我們已經在使用Hamcrest或要使用它來編寫單元測試,則可以通過以下Matchers#containsInAnyOrder方法進行不可知的訂單比較。

要在我們的Maven項目中使用Hamcrest,讓我們pom.xml文件中hamcrest-all依賴項:

<dependency>

 <groupId>org.hamcrest</groupId>

 <artifactId>hamcrest-all</artifactId>

 <version>1.3</version>

 </dependency>

讓我們看一下測試:

@Test

 public void whenTestingForOrderAgnosticEquality_ShouldBeEqual() {

 assertThat(first, Matchers.containsInAnyOrder(second.toArray()));

 }

在這裡,方法containsInAnyOrder Iterables創建了與訂單無關的匹配器,該匹配器與檢查的Iterable元素進行匹配。此測試匹配兩個列表中的元素,而忽略列表中元素的順序。

值得慶幸的是,該解決方案不會遇到與上一節所述相同的問題,因此我們不需要顯式比較大小。

6.使用Apache Commons

除了JUnit,Hamcrest或AssertJ之外,我們可以使用的另一個庫或框架是Apache CollectionUtils 。它提供了適用於廣泛使用案例的常用操作的實用方法,並幫助我們避免編寫樣板代碼。

要在我們的maven項目中使用它,讓我們pom.xml文件中commons-collections4依賴項:

<dependency>

 <groupId>org.apache.commons</groupId>

 <artifactId>commons-collections4</artifactId>

 <version>4.4</version>

 </dependency>

這是一個使用CollectionUtils的測試:

@Test

 public void whenTestingForOrderAgnosticEquality_ShouldBeTrueIfEqualOtherwiseFalse() {

 assertTrue(CollectionUtils.isEqualCollection(first, second));

 assertFalse(CollectionUtils.isEqualCollection(first, third));

 }

如果給定的集合包含具有相同基數isEqualCollection方法將返回true 。否則,它返回false

7.結論

在本文中,我們探討瞭如何檢查兩個List實例的相等性,這兩個List實例的順序不同。