web-dev-qa-db-ja.com

配列のリストの等価性を最新のJavaと比較する方法は?

配列のリストは2つあります。

外部ライブラリを使用せずに、これらの同等性と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;
}

または、より良い方法がない場合、それも有効な答えです。

おまけ:Java 9は、Javaer Java 8が提供できるものよりも良い方法を提供します。

編集:コメントを見て、この質問が適度に熱くなっているのを見て、better "は、配列の内容をチェックする前に、まずすべての配列の長さをチェックする必要があります。内側の配列が長い場合、不等式をより迅速に検出できる可能性があるためです。

68
hyde

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

2)はるかに単純なソリューション(Java 5+)で機能します):

return Arrays.deepEquals(list1.toArray(), list2.toArray());

新しい要件(含まれている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);
}

その場合の解決策は次のとおりです。

return equal(list1, list2, s -> s.length)
    && equal(list1, list2, Arrays::asList);
29

少なくともforループはストリーム化でき、次のようになります。

return (list1.size()==list2.size() &&
        IntStream.range(0, list1.size())
                 .allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
49
khelwood

https://stackoverflow.com/a/23529010/75518Zip(ラムダb93に由来する)関数を使用すると、コードは次のようになります。

boolean match = a.size() == b.size() && 
                Zip(a.stream(), b.stream(), Arrays::deepEquals).
                allMatch(equal -> equal)

更新

最初に配列のサイズをチェックし、次にコンテンツをチェックするために、これは考慮するソリューションになる可能性があります

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

couldリストがランダムアクセスリストである場合(したがって、getへの呼び出しが高速-一般に一定の時間)、以下をもたらす場合、ストリームを使用します。

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

ただし、そうでない実装(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;
}
15
Alexis C.

反復子を使用して、1つのリストにストリーミングし、他のリストの各要素と比較できます。

_Iterator<String[]> it = list1.iterator();
boolean match = list1.size() == list2.size() &&
                list2.stream().allMatch(a -> Arrays.equals(a, it.next()));
_

リストがRandomAccessであるかどうかは関係ないため、最初のリストでget(index)メソッドの代わりにイテレーターを使用することをお勧めします。

注:これは、sequentialストリームでのみ機能します。パラレルストリームを使用すると、誤った結果になります。


編集:質問の最後の編集によると、配列のすべてのペアの長さを事前に確認する方が良いことを示しています、事前に、私の以前のコードにわずかな変更を加えて達成しました:

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

ここでは、2つのイテレーターを使用し、_list2_を2回ストリーミングしていますが、配列の最初のペアの内容をチェックする前にすべての長さをチェックする他の方法はありません。長さのチェックはnullセーフですが、コンテンツのチェックは Arrays.equals(array1, array2) メソッドに委任されます。