Java ArrayList - Wie kann ich feststellen, ob zwei Listen gleich sind, wobei die Reihenfolge keine Rolle spielt?

133

Ich habe zwei ArrayLists vom Typ Answer(selbstgemachte Klasse).

Ich möchte die beiden Listen vergleichen, um festzustellen, ob sie denselben Inhalt enthalten, jedoch ohne dass die Reihenfolge von Bedeutung ist.

Beispiel:

//These should be equal.
ArrayList<String> listA = {"a", "b", "c"}
ArrayList<String> listB = {"b", "c", "a"}

List.equalsgibt an, dass zwei Listen gleich sind, wenn sie dieselbe Größe, denselben Inhalt und dieselbe Reihenfolge von Elementen enthalten. Ich möchte das Gleiche, aber ohne dass die Reihenfolge von Bedeutung ist.

Gibt es eine einfache Möglichkeit, dies zu tun? Oder muss ich eine verschachtelte for-Schleife erstellen und jeden Index beider Listen manuell überprüfen?

Hinweis: Ich kann sie nicht von ArrayListeinem anderen Listentyp ändern , das müssen sie bleiben.

iaacp
quelle
4
Siehe die Antwort auf diese Frage: stackoverflow.com/a/1075699/1133011
David Kroukamp
Siehe List.containsAll (Liste) in Java
Sahil Jain

Antworten:

130

Sie können beide Listen mit Collections.sort()der Methode equals sortieren und dann verwenden. Eine etwas bessere Lösung besteht darin, vor der Bestellung zu überprüfen, ob sie gleich lang sind. Wenn dies nicht der Fall ist, sind sie nicht gleich, sortieren und verwenden dann gleich. Wenn Sie beispielsweise zwei Listen mit Zeichenfolgen hätten, wäre dies ungefähr so:

public  boolean equalLists(List<String> one, List<String> two){     
    if (one == null && two == null){
        return true;
    }

    if((one == null && two != null) 
      || one != null && two == null
      || one.size() != two.size()){
        return false;
    }

    //to avoid messing the order of the lists we will use a copy
    //as noted in comments by A. R. S.
    one = new ArrayList<String>(one); 
    two = new ArrayList<String>(two);   

    Collections.sort(one);
    Collections.sort(two);      
    return one.equals(two);
}
Jacob Schön
quelle
20
Denken Sie daran, die Reihenfolge der ursprünglichen Liste nicht zu zerstören (wie dies der Collections.sortFall ist) - dh eine Kopie zu übergeben.
Arshajii
@ARS ja das ist ein definitiver Nebeneffekt, aber nur wenn es in ihrem speziellen Fall darauf ankommt.
Jacob Schoen
3
Sie können einfach hinzufügen one = new ArrayList<String>(one); two = new ArrayList<String>(two);, um die Argumente nicht zu ruinieren.
Arshajii
@jschoen Der Versuch, Collections.sort () auszuführen, gibt folgenden Fehler aus: Gebundene Nichtübereinstimmung: Die generische Methodensortierung (Liste <T>) vom Typ Sammlungen gilt nicht für die Argumente (ArrayList <Antwort>). Der abgeleitete Typ Antwort ist kein gültiger Ersatz für den begrenzten Parameter <T erweitert Vergleichbar <? Super T >>
iaacp
3
Die zweite "if" -Anweisung innerhalb der Funktion kann vereinfacht werden, if(one == null || two == null || one.size() != two.size()){ return false; }da Sie bereits prüfen, ob sowohl eins als auch zwei null sind
Hugo
149

Der wahrscheinlich einfachste Weg für eine Liste wäre:

listA.containsAll(listB) && listB.containsAll(listA)

quelle
5
Wo ist der Spaß dabei? In aller Ernsthaftigkeit ist dies jedoch wahrscheinlich die bessere Lösung.
Jacob Schoen
67
Hängt davon ab, ob [a, b, c]und ob [c, b, a, b]derselbe Inhalt vorliegt. Diese Antwort würde sagen, dass sie es tun, aber es könnte sein, dass sie es für das OP nicht tun (da eines ein Duplikat enthält und das andere nicht). Ganz zu schweigen von den Effizienzproblemen.
Yshavit
4
Verbesserung basierend auf Kommentaren - System.out.println (((l1.size () == l2.size ()) && l2.containsAll (l1) && l1.containsAll (l2));
Nrj
8
@Nrj, was ist mit [1,2,2] und [2,1,1]?
ROMANIA_engineer
3
Dieser Ansatz hat eine Komplexität von O (n ^ 2). Betrachten Sie zwei Listen in umgekehrter Reihenfolge, zum Beispiel: [1,2,3] und [3,2,1]. Für das erste Element müssen n Elemente gescannt werden, für das zweite n-1 Element und so weiter. Die Komplexität wird also in der Größenordnung von n ^ 2 liegen. Ich denke, ein besserer Weg wird sein, gleich zu sortieren und dann zu verwenden. Es wird Komplexität von O (n * log (n))
Puneet
90

Apache Commons Sammlungen zur Rettung noch einmal:

List<String> listA = Arrays.asList("a", "b", "b", "c");
List<String> listB = Arrays.asList("b", "c", "a", "b");
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // true

 

List<String> listC = Arrays.asList("a", "b", "c");
List<String> listD = Arrays.asList("a", "b", "c", "c");
System.out.println(CollectionUtils.isEqualCollection(listC, listD)); // false

Docs:

org.apache.commons.collections4.CollectionUtils

public static boolean isEqualCollection(java.util.Collection a,
                                        java.util.Collection b)

Gibt zurück, truewenn die angegebenen Collections genau dieselben Elemente mit genau denselben Kardinalitäten enthalten.

Das heißt, wenn die Kardinalität von e in a gleich der Kardinalität von e in b ist , für jedes Element e in a oder b .

Parameter:

  • a - Die erste Sammlung darf nicht sein null
  • b - Die zweite Sammlung darf nicht sein null

Rückgabe: trueWenn die Sammlungen dieselben Elemente mit denselben Kardinalitäten enthalten.

acdcjunior
quelle
Die Implementierung scheint der Antwort von DiddiZ mehr oder weniger ähnlich zu sein.
user227353
OK ... aber wie wäre es, die Schuldigen (Elemente, die den beiden Listen nicht gemeinsam sind) zu finden, wenn die Antwort lautet false? Siehe meine Antwort.
Mike Nagetier
implementation 'org.apache.commons:commons-collections4:4.3'habe mich mit einem Fehler linken Caused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process Pfad .
Aliton Oliveira
13
// helper class, so we don't have to do a whole lot of autoboxing
private static class Count {
    public int count = 0;
}

public boolean haveSameElements(final List<String> list1, final List<String> list2) {
    // (list1, list1) is always true
    if (list1 == list2) return true;

    // If either list is null, or the lengths are not equal, they can't possibly match 
    if (list1 == null || list2 == null || list1.size() != list2.size())
        return false;

    // (switch the two checks above if (null, null) should return false)

    Map<String, Count> counts = new HashMap<>();

    // Count the items in list1
    for (String item : list1) {
        if (!counts.containsKey(item)) counts.put(item, new Count());
        counts.get(item).count += 1;
    }

    // Subtract the count of items in list2
    for (String item : list2) {
        // If the map doesn't contain the item here, then this item wasn't in list1
        if (!counts.containsKey(item)) return false;
        counts.get(item).count -= 1;
    }

    // If any count is nonzero at this point, then the two lists don't match
    for (Map.Entry<String, Count> entry : counts.entrySet()) {
        if (entry.getValue().count != 0) return false;
    }

    return true;
}
cHao
quelle
Wow, war wirklich überrascht, dass dies schneller funktioniert als alle anderen Lösungen. Und es unterstützt Early-Out.
DiddiZ
Dies könnte auch in der zweiten Schleife kurzschließen, wenn es countnegativ wird, was den Schleifenkörper vereinfacht. if(!counts.containsKey(item) || --counts.get(item).count < 0) return false;Außerdem könnte die dritte Schleife vereinfacht werdenfor(Count c: counts.values()) if(c.count != 0) return false;
Holger
@Holger Ich hatte etwas Ähnliches wie das erste in Betracht gezogen (Entfernen der Zählung bei Null), was den gleichen Effekt hätte, aber auch die Schecks am Ende in "Rückgabe, ob countsleer" umwandelt), wollte aber nicht verschleiern Der Hauptpunkt: Die Verwendung einer Karte macht dies im Grunde genommen zu einem O (N + M) -Problem und ist der größte Einzelschub, den Sie wahrscheinlich bekommen werden.
CHao
@cHao Ja, Sie verdienen die Credits für den Hinweis auf eine Lösung mit einer besseren zeitlichen Komplexität als die vorherigen. Ich habe gerade darüber nachgedacht, weil es in letzter Zeit eine ähnliche Frage zu Iterables gibt. Da wir jetzt auch Java 8 haben, hat es sich gelohnt, es zu überdenken. Wenn Sie in der zweiten Schleife kurzschließen, wenn die Zahl negativ wird, ist die dritte Schleife veraltet. Darüber hinaus kann das Vermeiden des Boxens ein zweischneidiges Schwert sein, wobei die Verwendung neuer Map.mergeGanzzahlen in den meisten Anwendungsfällen einfacher und effizienter sein kann. Siehe auch diese Antwort
Holger
10

Ich würde sagen, diese Antworten verpassen einen Trick.

Bloch sagt in seinem wesentlichen, wunderbaren, prägnanten, effektiven Java in Punkt 47, Titel "Die Bibliotheken kennen und benutzen", "Zusammenfassend, erfinden Sie das Rad nicht neu". Und er gibt mehrere sehr klare Gründe an, warum nicht.

Hier gibt es einige Antworten, die Methoden aus CollectionUtilsder Apache Commons Collections-Bibliothek vorschlagen, aber keine hat die schönste und eleganteste Art gefunden, diese Frage zu beantworten :

Collection<Object> culprits = CollectionUtils.disjunction( list1, list2 );
if( ! culprits.isEmpty() ){
  // ... do something with the culprits, i.e. elements which are not common

}

Schuldige : dh die Elemente, die beiden nicht gemeinsam sind Lists. Die Festlegung , welche Schuldigen gehören list1und die zu list2relativ einfach verwendet CollectionUtils.intersection( list1, culprits )und CollectionUtils.intersection( list2, culprits ).
In Fällen wie {"a", "a", "b"} neigt es jedoch dazu, auseinanderzufallen.disjunction mit {"a", "b", "b"} auseinanderzufallen ... außer dies ist kein Fehler der Software, aber der Art der Feinheiten / Mehrdeutigkeiten der gewünschten Aufgabe inhärent.

Sie können den Quellcode (l. 287) jederzeit auf eine solche Aufgabe untersuchen, wie sie von den Apache-Ingenieuren erstellt wurde. Ein Vorteil der Verwendung ihres Codes besteht darin, dass er gründlich erprobt und getestet wurde und viele Randfälle und Fallstricke vorweggenommen und behandelt wurden. Sie können diesen Code bei Bedarf nach Herzenslust kopieren und optimieren.


NB Ich war zunächst enttäuscht, dass keine der CollectionUtilsMethoden eine überladene Version bietet, mit der Sie Ihre eigene auferlegen können Comparator(damit Sie sie equalsfür Ihre Zwecke neu definieren können ).

Aus Sammlungen4 4.0 gibt es jedoch eine neue Klasse, Equatordie "die Gleichheit zwischen Objekten vom Typ T bestimmt". Bei der Prüfung des Quellcodes von collection4 CollectionUtils.java scheinen sie dies mit einigen Methoden zu verwenden, aber soweit ich das beurteilen kann, gilt dies nicht für die Methoden oben in der Datei, die die CardinalityHelperKlasse ... verwenden umfassen disjunctionund intersection.

Ich vermute, dass die Apache-Leute noch nicht dazu gekommen sind, weil es nicht trivial ist: Sie müssten so etwas wie eine "AbstractEquatingCollection" -Klasse erstellen, die anstelle der inhärenten equalsund hashCodeMethoden ihrer Elemente diese verwenden müsste der Equatorfür alle grundlegenden Methoden, wie zum Beispiel add, containsusw. NB in der Tat , wenn Sie auf den Quellcode schauen, AbstractCollectionnicht implementiert add, noch seine abstrakte Subklassen wie AbstractSet... Sie bis in die konkreten Klassen wie warten HashSetund ArrayListvor addist implementiert. Ziemlich Kopfschmerzen.

In der Zwischenzeit diesen Raum beobachten, nehme ich an. Die naheliegende Zwischenlösung wäre, alle Ihre Elemente in eine maßgeschneiderte Wrapper-Klasse zu verpacken , die die gewünschte Gleichheit verwendet equalsund hashCodeimplementiert ... und dann Collectionsdiese Wrapper-Objekte zu manipulieren .

Mike Nagetier
quelle
Auch jemand weise sagte "Kennen Sie die Kosten der Abhängigkeit"
Stanislaw Baranski
@ StanislawBaranski Das ist ein interessanter Kommentar. Ist es ein Vorschlag, dass man nicht zu sehr von solchen Bibliotheken abhängig sein sollte? Wenn Sie ein Betriebssystem auf einem Computer verwenden, ist das schon ein großer Vertrauenssprung, nicht wahr? Der Grund, warum ich die Apache-Bibliotheken gerne benutze, ist, dass ich sie in der Tat als sehr hochwertig betrachte und davon ausgehe, dass ihre Methoden ihrem "Vertrag" entsprechen und gründlich getestet wurden. Wie viel Zeit würden Sie für die Entwicklung Ihres eigenen Codes benötigen, dem Sie mehr vertrauen? Das Kopieren des Codes aus den Open-Source-Apache-Bibliotheken und das Überprüfen des Codes könnte ein
Mike Nagetier
8

Wenn die Kardinalität von Elementen keine Rolle spielt (dh wiederholte Elemente werden als eins betrachtet), gibt es eine Möglichkeit, dies zu tun, ohne sortieren zu müssen:

boolean result = new HashSet<>(listA).equals(new HashSet<>(listB));

Dadurch wird Setaus jedem ein Out erstellt Listund dann verwendetHashSet ‚s equalsMethode , die (natürlich) missachtet Ordnung.

Wenn Kardinalität wichtig ist, müssen Sie sich auf Einrichtungen beschränken, die von bereitgestellt werden List; @ jschoens Antwort wäre in diesem Fall passender.

Isaac
quelle
Was ist, wenn listA = [a, b, c, c] und listB = [a, b, c]. Ergebnis wird wahr sein, aber Listen sind nicht gleich.
Nikolas
6

Das Konvertieren der Listen in Guavas Multiset funktioniert sehr gut. Sie werden unabhängig von ihrer Reihenfolge verglichen und doppelte Elemente werden ebenfalls berücksichtigt.

static <T> boolean equalsIgnoreOrder(List<T> a, List<T> b) {
    return ImmutableMultiset.copyOf(a).equals(ImmutableMultiset.copyOf(b));
}

assert equalsIgnoreOrder(ImmutableList.of(3, 1, 2), ImmutableList.of(2, 1, 3));
assert !equalsIgnoreOrder(ImmutableList.of(1), ImmutableList.of(1, 1));
Natix
quelle
6

Dies basiert auf der @ cHao-Lösung. Ich habe mehrere Korrekturen und Leistungsverbesserungen hinzugefügt. Dies läuft ungefähr doppelt so schnell wie die Lösung für gleich geordnete Kopien. Funktioniert für jeden Sammlungstyp. Leere Sammlungen und Null werden als gleich angesehen. Nutzen Sie zu Ihrem Vorteil;)

/**
 * Returns if both {@link Collection Collections} contains the same elements, in the same quantities, regardless of order and collection type.
 * <p>
 * Empty collections and {@code null} are regarded as equal.
 */
public static <T> boolean haveSameElements(Collection<T> col1, Collection<T> col2) {
    if (col1 == col2)
        return true;

    // If either list is null, return whether the other is empty
    if (col1 == null)
        return col2.isEmpty();
    if (col2 == null)
        return col1.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (col1.size() != col2.size())
        return false;

    // Helper class, so we don't have to do a whole lot of autoboxing
    class Count
    {
        // Initialize as 1, as we would increment it anyway
        public int count = 1;
    }

    final Map<T, Count> counts = new HashMap<>();

    // Count the items in col1
    for (final T item : col1) {
        final Count count = counts.get(item);
        if (count != null)
            count.count++;
        else
            // If the map doesn't contain the item, put a new count
            counts.put(item, new Count());
    }

    // Subtract the count of items in col2
    for (final T item : col2) {
        final Count count = counts.get(item);
        // If the map doesn't contain the item, or the count is already reduced to 0, the lists are unequal 
        if (count == null || count.count == 0)
            return false;
        count.count--;
    }

    // At this point, both collections are equal.
    // Both have the same length, and for any counter to be unequal to zero, there would have to be an element in col2 which is not in col1, but this is checked in the second loop, as @holger pointed out.
    return true;
}
DiddiZ
quelle
Sie können die letzte for-Schleife mit einem Summenzähler überspringen. Der Summenzähler zählt die Gesamtzahl der Zählungen in jeder Stufe. Erhöhen Sie den Summenzähler in der ersten for-Schleife und verringern Sie ihn in der zweiten for-Schleife. Wenn der Summenzähler größer als 0 ist, stimmen die Listen nicht überein, andernfalls. Derzeit prüfen Sie in der letzten for-Schleife, ob alle Zählungen Null sind oder mit anderen Worten, ob die Summe aller Zählungen Null ist. Die Verwendung des Summenzählers kehrt diese Prüfung um und gibt true zurück, wenn die Gesamtzahl der Zählungen Null ist, oder andernfalls false.
SatA
IMO, es lohnt sich, diese for-Schleife zu überspringen, da die for-Schleife ein weiteres unnötiges O (n) hinzufügt, wenn die Listen übereinstimmen (Worst-Case-Szenario).
SatA
@SatA tatsächlich können Sie die 3. Schleife ohne Ersatz entfernen. Die zweite Schleife kehrt bereits zurück, falsewenn ein Schlüssel nicht vorhanden ist oder seine Anzahl negativ wird. Da die Gesamtgröße beider Listen übereinstimmt (dies wurde im Voraus überprüft), ist es unmöglich, nach der zweiten Schleife Werte ungleich Null zu haben, da es für einen Schlüssel keine positiven Werte ohne negative Werte für einen anderen Schlüssel geben kann.
Holger
@holger es scheint, dass Sie absolut richtig sind. Soweit ich das beurteilen kann, ist die 3. Schleife überhaupt nicht notwendig.
Samstag,
@SatA… und mit Java 8 kann dies wie in dieser Antwort präzise implementiert werden .
Holger
5

Überlegen Sie, wie Sie dies selbst tun würden, wenn Sie keinen Computer oder keine Programmiersprache hätten. Ich gebe Ihnen zwei Listen von Elementen, und Sie müssen mir sagen, ob sie die gleichen Elemente enthalten. Wie würdest du es machen?

Wie oben erwähnt, besteht ein Ansatz darin, die Listen zu sortieren und dann Element für Element zu prüfen, ob sie gleich sind (was auch der List.equalsFall ist). Dies bedeutet, dass Sie entweder die Listen ändern oder kopieren dürfen - und ohne die Zuordnung zu kennen, kann ich nicht wissen, ob eine oder beide zulässig sind.

Ein anderer Ansatz wäre, jede Liste durchzugehen und zu zählen, wie oft jedes Element erscheint. Wenn beide Listen am Ende die gleiche Anzahl haben, haben sie die gleichen Elemente. Der Code dafür wäre, jede Liste in eine Karte von zu übersetzen elem -> (# of times the elem appears in the list)und dann equalsdie beiden Karten aufzurufen . Wenn die Karten sind HashMap, ist jede dieser Übersetzungen eine O (N) -Operation, ebenso wie der Vergleich. Das gibt Ihnen einen ziemlich effizienten Algorithmus in Bezug auf die Zeit auf Kosten von zusätzlichem Speicher.

yshavit
quelle
5

Ich hatte das gleiche Problem und fand eine andere Lösung. Dieser funktioniert auch, wenn Duplikate beteiligt sind:

public static boolean equalsWithoutOrder(List<?> fst, List<?> snd){
  if(fst != null && snd != null){
    if(fst.size() == snd.size()){
      // create copied lists so the original list is not modified
      List<?> cfst = new ArrayList<Object>(fst);
      List<?> csnd = new ArrayList<Object>(snd);

      Iterator<?> ifst = cfst.iterator();
      boolean foundEqualObject;
      while( ifst.hasNext() ){
        Iterator<?> isnd = csnd.iterator();
        foundEqualObject = false;
        while( isnd.hasNext() ){
          if( ifst.next().equals(isnd.next()) ){
            ifst.remove();
            isnd.remove();
            foundEqualObject = true;
            break;
          }
        }

        if( !foundEqualObject ){
          // fail early
          break;
        }
      }
      if(cfst.isEmpty()){ //both temporary lists have the same size
        return true;
      }
    }
  }else if( fst == null && snd == null ){
    return true;
  }
  return false;
}

Vorteile gegenüber einigen anderen Lösungen:

  • weniger als O (N²) -Komplexität (obwohl ich die tatsächliche Leistung im Vergleich zu Lösungen in anderen Antworten hier nicht getestet habe);
  • geht früh;
  • prüft auf null;
  • funktioniert auch, wenn Duplikate beteiligt sind: Wenn Sie ein Array [1,2,3,3]und ein anderes Array [1,2,2,3]haben, sagen Ihnen die meisten Lösungen hier, dass sie gleich sind, wenn Sie die Reihenfolge nicht berücksichtigen. Diese Lösung vermeidet dies, indem gleiche Elemente aus den temporären Listen entfernt werden.
  • verwendet semantische Gleichheit ( equals) und nicht Referenzgleichheit ( ==);
  • sortiert itens nicht, daher müssen sie nicht sortierbar sein, damit implement Comparablediese Lösung funktioniert.
Chalkos
quelle
3

Wenn Sie nicht hoffen, die Sammlungen zu sortieren, und Sie das Ergebnis benötigen, dass ["A" "B" "C"] nicht gleich ["B" "B" "A" "C"] ist,

l1.containsAll(l2)&&l2.containsAll(l1)

ist nicht genug, Sie müssen wahrscheinlich auch die Größe überprüfen:

    List<String> l1 =Arrays.asList("A","A","B","C");
    List<String> l2 =Arrays.asList("A","B","C");
    List<String> l3 =Arrays.asList("A","B","C");

    System.out.println(l1.containsAll(l2)&&l2.containsAll(l1));//cautions, this will be true
    System.out.println(isListEqualsWithoutOrder(l1,l2));//false as expected

    System.out.println(l3.containsAll(l2)&&l2.containsAll(l3));//true as expected
    System.out.println(isListEqualsWithoutOrder(l2,l3));//true as expected


    public static boolean isListEqualsWithoutOrder(List<String> l1, List<String> l2) {
        return l1.size()==l2.size() && l1.containsAll(l2)&&l2.containsAll(l1);
}
Jaskey
quelle
2

Lösung, die die Subtraktionsmethode CollectionUtils nutzt:

import static org.apache.commons.collections15.CollectionUtils.subtract;

public class CollectionUtils {
  static public <T> boolean equals(Collection<? extends T> a, Collection<? extends T> b) {
    if (a == null && b == null)
      return true;
    if (a == null || b == null || a.size() != b.size())
      return false;
    return subtract(a, b).size() == 0 && subtract(a, b).size() == 0;
  }
}
Maxdanilkin
quelle
1

Wenn Sie sich für die Bestellung interessieren, verwenden Sie einfach die Methode equals:

list1.equals(list2)

Wenn es Ihnen egal ist, bestellen Sie diese

Collections.sort(list1);
Collections.sort(list2);      
list1.equals(list2)
Ramesh Kumar
quelle
4
Er sagt, er kümmere sich nicht um Ordnung.
Mike Nagetier
0

Das Beste aus beiden Welten [@DiddiZ, @Chalkos]: Diese basiert hauptsächlich auf der @ Chalkos-Methode, behebt jedoch einen Fehler (ifst.next ()) und verbessert die anfänglichen Überprüfungen (aus @DiddiZ) sowie die Notwendigkeit Kopieren Sie die erste Sammlung (entfernt nur Elemente aus einer Kopie der zweiten Sammlung).

Dies ist die bisher effizienteste Implementierung, die keine Hashing-Funktion oder Sortierung erfordert und eine frühzeitige Existenz bei Ungleichheit ermöglicht. Es sei denn, Sie haben eine Sammlungslänge von Tausenden oder mehr und eine sehr einfache Hashing-Funktion.

public static <T> boolean isCollectionMatch(Collection<T> one, Collection<T> two) {
    if (one == two)
        return true;

    // If either list is null, return whether the other is empty
    if (one == null)
        return two.isEmpty();
    if (two == null)
        return one.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (one.size() != two.size())
        return false;

    // copy the second list, so it can be modified
    final List<T> ctwo = new ArrayList<>(two);

    for (T itm : one) {
        Iterator<T> it = ctwo.iterator();
        boolean gotEq = false;
        while (it.hasNext()) {
            if (itm.equals(it.next())) {
                it.remove();
                gotEq = true;
                break;
            }
        }
        if (!gotEq) return false;
    }
    // All elements in one were found in two, and they're the same size.
    return true;
}
Jazzgil
quelle
Wenn ich mich nicht irre, wäre die Komplexität dieses Algorithmus in einem wertvollen Szenario (in dem die Listen gleich, aber umgekehrt sortiert sind) O (N * N!).
SatA
Tatsächlich wäre es O (N * (N / 2)), da mit jeder Iteration die Arraygröße abnimmt.
Jazzgil
0

Es ist eine alternative Möglichkeit, die Gleichheit von Array-Listen zu überprüfen, die Nullwerte enthalten können:

List listA = Arrays.asList(null, "b", "c");
List listB = Arrays.asList("b", "c", null);

System.out.println(checkEquality(listA, listB)); // will return TRUE


private List<String> getSortedArrayList(List<String> arrayList)
{
    String[] array = arrayList.toArray(new String[arrayList.size()]);

    Arrays.sort(array, new Comparator<String>()
    {
        @Override
        public int compare(String o1, String o2)
        {
            if (o1 == null && o2 == null)
            {
                return 0;
            }
            if (o1 == null)
            {
                return 1;
            }
            if (o2 == null)
            {
                return -1;
            }
            return o1.compareTo(o2);
        }
    });

    return new ArrayList(Arrays.asList(array));
}

private Boolean checkEquality(List<String> listA, List<String> listB)
{
    listA = getSortedArrayList(listA);
    listB = getSortedArrayList(listB);

    String[] arrayA = listA.toArray(new String[listA.size()]);
    String[] arrayB = listB.toArray(new String[listB.size()]);

    return Arrays.deepEquals(arrayA, arrayB);
}
Ayaz Alifov
quelle
Was bringt all das Kopieren zwischen Listen und Arrays?
Holger
0

Meine Lösung dafür. Es ist nicht so cool, funktioniert aber gut.

public static boolean isEqualCollection(List<?> a, List<?> b) {

    if (a == null || b == null) {
        throw new NullPointerException("The list a and b must be not null.");
    }

    if (a.size() != b.size()) {
        return false;
    }

    List<?> bCopy = new ArrayList<Object>(b);

    for (int i = 0; i < a.size(); i++) {

        for (int j = 0; j < bCopy.size(); j++) {
            if (a.get(i).equals(bCopy.get(j))) {
                bCopy.remove(j);
                break;
            }
        }
    }

    return bCopy.isEmpty();
}
Cícero Moura
quelle
-1

In diesem Fall sind die Listen {"a", "b"} und {"b", "a"} gleich. Und {"a", "b"} und {"b", "a", "c"} sind nicht gleich. Wenn Sie die Liste von komplexen Objekten verwenden, denken Sie daran, Überschreibung gleich Methode, wie containsAll es im Inneren verwendet.

if (oneList.size() == secondList.size() && oneList.containsAll(secondList)){
        areEqual = true;
}
Andrew
quelle
-1: gibt die falsche Antwort mit {"a", "a", "b"} und {"a", "b", "b"}: check out source code for AbstractCollection.containsAll(). Sie müssen zulassen, dass doppelte Elemente vorhanden sind, über die wir sprechen Lists, nicht über Sets. Bitte sehen Sie meine Antwort.
Mike Nagetier