Ich habe zwei Listen von Arrays.
Wie kann ich die Gleichheit dieser mit Java 8 und seinen Funktionen vergleichen , ohne externe Bibliotheken zu verwenden? Ich suche nach einer "besseren" (übergeordneten, kürzeren, effizienteren) Lösung als Brute-Force-Code wie diesem (ungetesteter Code, kann Tippfehler usw. enthalten, nicht der Punkt der Frage):
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;
}
Oder wenn es keinen schöneren Weg gibt, ist das auch eine gültige Antwort.
Bonus: Wenn Java 9 eine noch bessere Möglichkeit bietet, was auch immer Java 8 bieten kann, können Sie es auch erwähnen.
Bearbeiten: Nachdem ich mir die Kommentare angesehen und gesehen habe, wie diese Frage mäßig heiß geworden ist, denke ich, dass das " Bessere " das erste Überprüfen der Länge aller Arrays vor dem Überprüfen des Array-Inhalts beinhalten sollte , da dies das Potenzial hat, Ungleichheit viel schneller zu finden, wenn sie innerlich ist Arrays sind lang.
Antworten:
1) Lösung basierend auf Java 8-Streams:
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) Viel einfachere Lösung (funktioniert in Java 5+):
return Arrays.deepEquals(list1.toArray(), list2.toArray());
3) In Bezug auf Ihre neue Anforderung (um zuerst die Länge der enthaltenen String-Arrays zu überprüfen) können Sie eine generische Hilfsmethode schreiben, die die Gleichheitsprüfung für transformierte Listen durchführt:
<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); }
Dann könnte die Lösung sein:
return equal(list1, list2, s -> s.length) && equal(list1, list2, Arrays::asList);
quelle
deepEquals
prüft auf Länge. Aus dem Javadoc: Zwei Array-Referenzen werden als zutiefst gleich angesehen, wenn beide null sind oder wenn sie sich auf Arrays beziehen, die die gleiche Anzahl von Elementen enthalten und alle entsprechenden Elementpaare in den beiden Arrays zutiefst gleich sind.Die
for
Schleife kann zumindest gestreamt werden, was zu Folgendem führt:return (list1.size()==list2.size() && IntStream.range(0, list1.size()) .allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
quelle
RandomAccess
Listen ineffizient ist .zip
ist für solche Probleme viel natürlicher und eine bekannte Redewendung in vielen verschiedenen Sprachen - wie in hahns Antwort dargestellt . In einigen Fällen ganz zu schweigen von der Leistungsüberlegenheit (wie von @JaroslawPawlak im oberen Kommentar erwähnt).Mit der Funktion
zip
(die von Lambda B93 stammt) von https://stackoverflow.com/a/23529010/755183 könnte der Code wie folgt aussehen:boolean match = a.size() == b.size() && zip(a.stream(), b.stream(), Arrays::deepEquals). allMatch(equal -> equal)
aktualisieren
Um zuerst die Größe der Arrays und dann den Inhalt zu überprüfen, könnte dies eine zu berücksichtigende Lösung sein
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);
quelle
Sie können einen Stream verwenden, wenn es sich bei den Listen um Listen mit wahlfreiem Zugriff handelt (so dass ein Aufruf von
get
schnell ist - im Allgemeinen eine konstante Zeit). Dies führt zu://checks for null and size before boolean same = IntStream.range(0, list1.size()).allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
Sie können jedoch einige Implementierungen als Parameter angeben, die dies nicht sind (z. B. LinkedLists). In diesem Fall ist es am besten, den Iterator explizit zu verwenden. Etwas wie:
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; }
quelle
return true;
:)&& iteList2.hasNext()
in der Bedingung weglassenSie können über eine Liste streamen und mit jedem Element des anderen vergleichen, indem Sie einen Iterator verwenden:
Iterator<String[]> it = list1.iterator(); boolean match = list1.size() == list2.size() && list2.stream().allMatch(a -> Arrays.equals(a, it.next()));
Die Verwendung eines Iterators anstelle der
get(index)
Methode in der ersten Liste ist besser, da es keine Rolle spielt, ob die Liste vorhanden istRandomAccess
oder nicht.Hinweis: Dies funktioniert nur mit einem sequentiellen Stream. Die Verwendung eines parallelen Streams führt zu falschen Ergebnissen.
BEARBEITEN: Gemäß der Frage der letzten Bearbeitung, die angibt, dass es besser ist, die Länge jedes Array-Paares im Voraus zu überprüfen , denke ich, dass dies mit einer geringfügigen Änderung meines vorherigen Codes erreicht werden könnte:
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()));
Hier verwende ich zwei Iteratoren und streame
list2
zweimal, aber ich sehe keine andere Möglichkeit, alle Längen zu überprüfen, bevor der Inhalt des ersten Array-Paares überprüft wird. Die Prüfung auf Längen ist nullsicher, während die Prüfung auf Inhalt an dieArrays.equals(array1, array2)
Methode delegiert wird.quelle
.parallelStream()
.list2.stream() .allMatch(a -> Arrays.equals(a, it.next()))
.