如何比较数组列表与现代Java的相等性?

hyd*_*yde 68 java arrays list equals java-8

我有两个数组列表.

如何在不使用外部库的情况下轻松地将这些与Java 8及其功能的相等性进行比较?我正在寻找一个"更好"(更高级别,更短,更高效)的解决方案,而不是像这样的强力代码(未经测试的代码,可能包含错别字等,而不是问题的重点):

boolean compare(List<String[]> list1, List<String[]> list2) 
{
    // tests for nulls etc omitted
    if(list1.size() != list2.size()) {
       return false;
    }
    for(i=0; i<list1.size(); ++i) {
        if(!Arrays.equals(list1.get(i), list2.get(i))) {
            return false;
        }
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果没有更好的方法,这也是一个有效的答案.

额外奖励:如果Java 9提供了更好的Java 8可以提供的方式,请随时提及它.

编辑:看完评论后,看看这个问题是如何变得适度热,我认为" 更好 "应该包括在检查数组内容之前首先检查所有数组的长度,因为这有可能更快地找到不等式,如果内在的话数组很长.

khe*_*ood 49

for环至少可以streamified,导致:

return (list1.size()==list2.size() &&
        IntStream.range(0, list1.size())
                 .allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
Run Code Online (Sandbox Code Playgroud)

  • @ymbirtt:实际上,有一种方法,但它需要你编写自己的集合或扩展现有集合并进行修改.只需在集合上保留哈希码即可.每次添加项目时,相应地调整哈希值(例如,乘以素数并添加项目的哈希码).然后你要做的就是比较2个集合的哈希值,即O(1).由于存在碰撞的可能性,您可以选择仅在哈希匹配时进行完全比较,因此它会为O(n)然后在其余时间更快.此外,删除或编辑集合中的项目可能很复杂. (17认同)
  • 非常好的解决方案.值得补充的是,它对于非"RandomAccess"列表来说是低效的. (12认同)
  • @cat,在一般情况下,没有办法比O(n)更快地比较n个元素的两个集合. (10认同)
  • @DarrelHoffman为了准确比较,你必须*比较哈希匹配时的每个元素. (8认同)
  • 这是O(1)吗? (2认同)

hah*_*ahn 31

使用zip(源自lambda b93)函数来自/sf/answers/1647030731/,代码可能如下所示:

boolean match = a.size() == b.size() && 
                zip(a.stream(), b.stream(), Arrays::deepEquals).
                allMatch(equal -> equal)
Run Code Online (Sandbox Code Playgroud)

更新

为了首先检查数组的大小,然后内容这可能是一个需要考虑的解决方案

final boolean match = a.size() == b.size() 
                   && zip(a.stream(), b.stream(), (as, bs) -> as.length == bs.length).
                      allMatch(equal -> equal)
                   && zip(a.stream(), b.stream(), Arrays::deepEquals).
                      allMatch(equal -> equal);
Run Code Online (Sandbox Code Playgroud)


Dra*_*vic 31

1)基于Java 8流的解决方案:

List<List<String>> first = list1.stream().map(Arrays::asList).collect(toList());
List<List<String>> second = list2.stream().map(Arrays::asList).collect(toList());
return first.equals(second);
Run Code Online (Sandbox Code Playgroud)

2)更简单的解决方案(适用于Java 5+):

return Arrays.deepEquals(list1.toArray(), list2.toArray());
Run Code Online (Sandbox Code Playgroud)

3)关于您的新要求(首先检查包含的String数组长度),您可以编写一个通用的帮助器方法,对转换后的列表进行相等性检查:

<T, U> boolean equal(List<T> list1, List<T> list2, Function<T, U> mapper) {
    List<U> first = list1.stream().map(mapper).collect(toList());
    List<U> second = list2.stream().map(mapper).collect(toList());
    return first.equals(second);
}
Run Code Online (Sandbox Code Playgroud)

然后解决方案可能是:

return equal(list1, list2, s -> s.length)
    && equal(list1, list2, Arrays::asList);
Run Code Online (Sandbox Code Playgroud)

  • #2更简单,但也创建了每个列表的副本...... (5认同)
  • @Holger True,实际上第一个解决方案也涉及复制列表.但是包含的String数组不会在两个解决方案中的任何一个中复制,只会复制对它们的引用. (2认同)
  • @hyde`deepEquals`会检查长度.来自javadoc:*如果两个数组引用都为null,或者它们引用包含**相同数量的元素的数组**,并且两个数组中所有相应的元素对完全相等,则认为两个数组引用完全相等.* (2认同)

Ale*_* C. 15

如果列表是随机访问列表(因此调用快速 - 通常是恒定时间),您可以使用流,从​​而get导致:

//checks for null and size before
boolean same = IntStream.range(0, list1.size()).allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
Run Code Online (Sandbox Code Playgroud)

但是,您可以为某些非实现的实现(例如LinkedLists)提供参数.在这种情况下,最好的方法是显式使用迭代器.就像是:

boolean compare(List<String[]> list1, List<String[]> list2) {

    //checks for null and size

    Iterator<String[]> iteList1 = list1.iterator();
    Iterator<String[]> iteList2 = list2.iterator();

    while(iteList1.hasNext()) {
        if(!Arrays.equals(iteList1.next(), iteList2.next())) {
            return false;
        }
    }
    return true;
}
Run Code Online (Sandbox Code Playgroud)


Fed*_*ner 6

您可以使用迭代器对一个列表进行流式处理并与另一个列表的每个元素进行比较:

Iterator<String[]> it = list1.iterator();
boolean match = list1.size() == list2.size() &&
                list2.stream().allMatch(a -> Arrays.equals(a, it.next()));
Run Code Online (Sandbox Code Playgroud)

使用迭代器而不是get(index)第一个列表上的方法更好,因为列表是否存在并不重要RandomAccess.

注意:这仅适用于顺序流.使用并行流将导致错误的结果.


编辑:根据问题的最后一次编辑,这表明这将是更好地检查每对数组的长度事先,我认为它可能有轻微的修改我以前的代码来实现:

Iterator<String[]> itLength = list1.iterator();
Iterator<String[]> itContents = list1.iterator();

boolean match = 
        list1.size() == list2.size()
    && 
        list2.stream()
            .allMatch(a -> {
                String[] s = itLength.next();
                return s == null ? a == null :
                       a == null ? s == null :
                       a.length == s.length;
            })
    && 
        list2.stream()
            .allMatch(a -> Arrays.equals(a, itContents.next()));
Run Code Online (Sandbox Code Playgroud)

在这里,我使用两个迭代器,并且流式传输list2两次,但在检查第一对数组的内容之前,我看不到检查所有长度的其他方法.检查长度是否为空安全,而检查内容是否委托给Arrays.equals(array1, array2)方法.