Java忽略顺序比较两个List列表是否相等的正确方法

培训教学 潘老师 7个月前 (10-10) 382 ℃ (0) 扫码查看

学会如何在Java中比较两个List列表,以便判断两个列表包含完全相同的项目,而且每个列表项在两个列表中的出现次数必须相等。

1.使用Common Collections4的CollectionUtils.isEqualCollection()

要使用此API,请在Maven存储库中包含最新版本的commons-collections4。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

isEqualCollection() API在两个列表完全包含相同元素且具有完全相同基数(即:一个元素在两个列表中的出现次数完全相同)时返回true。

List<String> list = Arrays.asList("a", "b", "c");
List<String> equalList = Arrays.asList("b", "c", "a");
List<String> unequalList = Arrays.asList("d", "c", "a");
assertTrue(CollectionUtils.isEqualCollection(list, equalList));
assertFalse(CollectionUtils.isEqualCollection(list, unequalList));

我们可以验证在比较列表时也考虑了基数。

assertFalse(CollectionUtils.isEqualCollection(
    Arrays.asList("a", "a", "b"),
    Arrays.asList("a", "b", "b")));
assertTrue(CollectionUtils.isEqualCollection(
    Arrays.asList("a", "b", "b"),
    Arrays.asList("a", "b", "b")));

默认情况下,使用equals()方法比较列表项。有时,我们从不同的来源接收列表,而列表项可能不包含默认相等规则所需的所有字段。我们还可以通过提供Equator接口的自定义实现来提供自定义相等逻辑。

在以下示例中,我们正在比较String实例,忽略大小写。请注意,使用Map实例检查基数,因此有必要将相同的项目转换为相同的键。在这种情况下,所有键将转换为大写字符串。

class StringCaseEquator implements Equator<String> {
  public boolean equate(String s1, String s2) {
    return s1.equalsIgnoreCase(s2);
  }
  @Override
  public int hash(String s) {
    return s.toUpperCase().hashCode();
  }
}
assertTrue(CollectionUtils.isEqualCollection(
    Arrays.asList("a", "b"),
    Arrays.asList("A", "B"),
    new StringCaseEquator()));

2.使用Hamcrest的Matchers.containsInAnyOrder()

要使用此API,请在Maven存储库中包含最新版本的hamcrest-all。

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
</dependency>

containsInAnyOrder() API创建了一个用于可迭代对象的无序匹配器,该匹配器匹配两个列表的元素,忽略列表中元素的顺序。对于积极匹配,两个列表必须具有相同的大小,因此我们不需要显式比较大小。

List<String> list = Arrays.asList("a", "b", "c");
List<String> equalList = Arrays.asList("b", "c", "a");
assertThat(list, Matchers.containsInAnyOrder(equalList.toArray()));

此解决方案不支持列表项的自定义相等逻辑。

3.使用AssertJ的containsExactlyInAnyOrderElementsOf() API

首先添加Maven存储库中最新版本的assertj-core。

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.16.1</version>
</dependency>

containsExactlyInAnyOrderElementsOf()验证第一个列表包含第二个列表的所有元素,并且每个元素以任意顺序出现相同数量的次数。

List<String> list = Arrays.asList("a", "b", "c");
List<String> equalList = Arrays.asList("b", "c", "a");
org.assertj.core.api.Assertions.assertThat(list)
        .containsExactlyInAnyOrderElementsOf(equalList);

任何额外出现的项目都将导致断言失败。

4.纯粹的Java或JUnit

如果我们不想使用任何第三方库,可以使用List.remove()方法来设计我们自己的解决方案。在以下代码中,我们正在迭代第一个列表,并从第二个列表中删除当前项目的第一个出现。迭代完成后,第二个列表必须为空。请注意,此方法使用ArrayList类上提供的remove() API。因此,如果使用不可变列表,请考虑将它们包装在ArrayList实例中。

static boolean compareListsIgnoringOrder(ArrayList list1, ArrayList list2) {
  if (list1 == null || list2 == null) return false;
  if (list1.size() != list2.size()) return false;
  for (Object o : list1) {
    list2.remove(o);
  }
  if (list2.size() != 0) return false;
  return true;
}
List<String> firstList = Arrays.asList("a", "b", "c", "c");
List<String> secondList = Arrays.asList("b", "c", "a", "c");
assertTrue(compareListsIgnoringOrder(new ArrayList(firstList), new ArrayList<>(secondList)));

5.结论

在单元测试中,需要比较两个Java列表的顺序是否被忽略,这是一个常见的需求,其中两个列表来自不同的来源,我们必须检查两个列表是否具有相同的元素,而不考虑它们在列表中的顺序。本教程展示了使用Apache Common Collections4、Hamcrest、AssertJ和纯粹的Java API的示例,可用于JUnit断言语句。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/teach/9419.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】