web-dev-qa-db-fra.com

Un moyen simple de trouver si deux listes différentes contiennent exactement les mêmes éléments?

Quel est le moyen le plus simple de déterminer si deux listes contiennent exactement les mêmes éléments, dans les bibliothèques Java standard? 

Peu importe que les deux listes soient ou non la même instance et que le paramètre de type des listes soit différent.

par exemple.

List list1
List<String> list2; 
// ... construct etc

list1.add("A");
list2.add("A"); 
// the function, given these two lists, should return true

Il y a probablement quelque chose qui me regarde en face, je sais :-)


EDIT: Pour clarifier, je cherchais EXACTE les mêmes éléments et le nombre d’éléments, dans l’ordre.


EDIT: Merci de signaler la réponse évidente que je ne pouvais pas voir pour regarder :-)

Bien que toutes les réponses données à ce jour soient correctes, certaines le sont plus que d'autres, alors j'attendrai un peu la réponse la plus complète avant d'accepter.

207
Grundlefleck

Si vous vous souciez de l'ordre, utilisez simplement la méthode equals:

list1.equals(list2)

Du javadoc:

Compare l'objet spécifié avec cette liste pour l'égalité. Renvoie true si et seulement si l'objet spécifié est aussi une liste, les deux listes ont le même taille, et toutes les paires correspondantes de les éléments des deux listes sont égaux . (Deux éléments e1 et e2 sont égaux si (E1 == null? E2 == null: E1.equals (e2)).) En d'autres termes, deux les listes sont définies comme étant égales si elles contient les mêmes éléments dans le même ordre. Cette définition garantit que la méthode equals fonctionne correctement à travers différentes implémentations de l'interface de liste.

Si vous souhaitez vérifier indépendamment de l'ordre, vous pouvez copier tous les éléments dans des ensembles et utiliser un égal pour les ensembles résultants:

public static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}

Une limite de cette approche est qu’elle ignore non seulement l’ordre, mais aussi la fréquence des éléments en double. Par exemple, si list1 était ["A", "B", "A"] et que list2 était ["A", "B", "B"], l'approche Set les considérerait égaux.

Si vous avez besoin d’être insensible à la commande mais sensible à la fréquence des doublons, vous pouvez soit:

298
Laurence Gonsalves

J'ai posté un tas de choses dans des commentaires, je pense que cela mérite sa propre réponse.

Comme tout le monde le dit ici, utiliser equals () dépend de l'ordre. Si vous ne vous souciez pas de l'ordre, vous avez 3 options.

Option 1

Utilisez containsAll(). À mon avis, cette option n’est pas idéale, car elle offre les meilleures performances, O (n ^ 2).

Option 2

Il y a deux variations à cela:

2a) Si vous ne vous souciez pas de maintenir l'ordre de vos listes ... utilisez Collections.sort() sur les deux listes. Puis utilisez le equals(). C'est O (nlogn), car vous faites deux tris, puis une comparaison O(n).

2b) Si vous devez conserver l'ordre des listes, vous pouvez commencer par copier les deux listes. ALORS, vous pouvez utiliser la solution 2a sur les deux listes copiées. Cependant, cela peut paraître peu attrayant si la copie coûte très cher.

Cela mène à:

Option 3

Si vos exigences sont identiques à celles de la partie 2b, la copie est trop chère. Vous pouvez utiliser un TreeSet pour effectuer le tri à votre place. Videz chaque liste dans son propre TreeSet. Elle sera triée dans l’ensemble et les listes originales resteront intactes. Effectuez ensuite une comparaison equals() sur les deux TreeSets. Les TreeSetss peuvent être construits en O(nlogn) fois et le equals() est O (n).

Faites votre choix :-).

EDIT: J'ai presque oublié la même mise en garde que Laurence Gonsalves souligne. L’implémentation de TreeSet éliminera les doublons. Si vous vous souciez des doublons, vous aurez besoin d'une sorte de multiset trié.

78
Tom

Si vous utilisez (ou préférez utiliser) Apache Commons Collections, vous pouvez utiliser CollectionUtils.isEqualCollection qui "retourne vrai si les Collections données contiennent exactement les mêmes éléments avec exactement les mêmes cardinalités".

12
megaflop

Je sais que c’est un vieux sujet, mais aucune des autres réponses n’a complètement résolu mon cas d’utilisation (je suppose que Guava Multiset pourrait faire de même, mais il n’ya pas d’exemple ici). S'il vous plaît excuser ma mise en forme. Je suis encore nouveau pour poster sur l'échange de pile. De plus laissez-moi savoir s'il y a des erreurs 

Disons que vous avez List<T> a et List<T> b et que vous voulez vérifier si elles remplissent les conditions suivantes:

1) O(n) temps d'exécution prévu
2) L’égalité est définie comme suit: Pour tous les éléments de a ou b, le nombre de fois où l’élément apparaît dans a est égal au nombre de fois où l’élément se produit dans b. L'égalité des éléments est définie en tant que T.equals ()

private boolean listsAreEquivelent(List<? extends Object> a, List<? extends Object> b) {
    if(a==null) {
        if(b==null) {
            //Here 2 null lists are equivelent. You may want to change this.
            return true;
        } else {
            return false;
        }
    }
    if(b==null) {
        return false;
    }
    Map<Object, Integer> tempMap = new HashMap<>();
    for(Object element : a) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            tempMap.put(element, 1);
        } else {
            tempMap.put(element, currentCount+1);
        }
    }
    for(Object element : b) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            return false;
        } else {
            tempMap.put(element, currentCount-1);
        }
    }
    for(Integer count : tempMap.values()) {
        if(count != 0) {
            return false;
        }
    }
    return true;
}

Le temps d'exécution est O(n) car nous faisons des insertions de O (2 * n) dans une table de hachage et des sélections de O (3 * n). Je n'ai pas complètement testé ce code, alors méfiez-vous :)

//Returns true:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","A"));
listsAreEquivelent(null,null);
//Returns false:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),null);
6
Andrew

Très tard à la fête, mais je voulais ajouter ce chèque sans danger:

Objects.equals(list1, list2)
5
Reimeus

Essayez cette version qui ne nécessite pas que l'ordre soit identique, mais qui prend en charge plusieurs valeurs identiques. Ils ne correspondent que si chacun a la même quantité de valeur.

public boolean arraysMatch(List<String> elements1, List<String> elements2) {
    // Optional quick test since size must match
    if (elements1.size() != elements2.size()) {
        return false;
    }
    List<String> work = newArrayList(elements2);
    for (String element : elements1) {
        if (!work.remove(element)) {
            return false;
        }
    }
    return work.isEmpty();
}
4
Lee Meador

La réponse de Tom est excellente, je suis tout à fait d'accord avec ses réponses!

Un aspect intéressant de cette question est de savoir si vous avez besoin du type List et de son ordre inhérent.

Si ce n'est pas le cas, vous pouvez passer à Iterable ou Collection, ce qui vous donne une certaine souplesse pour faire circuler des structures de données triées au moment de l'insertion, plutôt qu'au moment où vous souhaitez vérifier.

Si l'ordre n'a jamais d'importance (et que vous n'avez pas d'éléments en double), envisagez d'utiliser une variable Set.

Si l'ordre est important mais qu'il est défini par le temps d'insertion (et que vous n'avez pas de doublons), considérez une variable LinkedHashSet qui ressemble à un TreeSet mais est classée par temps d'insertion (les doublons ne sont pas comptés). Cela vous donne également O(1) accès amorti au lieu de O(log n).

2
Alex

Solution pour le cas où deux listes ont les mêmes éléments, mais un ordre différent:

public boolean isDifferentLists(List<Integer> listOne, List<Integer> listTwo) {
    if(isNullLists(listOne, listTwo)) {
        return false;
    }

    if (hasDifferentSize(listOne, listTwo)) {
        return true;
    }

    List<Integer> listOneCopy = Lists.newArrayList(listOne);
    List<Integer> listTwoCopy = Lists.newArrayList(listTwo);
    listOneCopy.removeAll(listTwoCopy);

    return CollectionUtils.isNotEmpty(listOneCopy);
}

private boolean isNullLists(List<Integer> listOne, List<Integer> listTwo) {
    return listOne == null && listTwo == null;
}

private boolean hasDifferentSize(List<Integer> listOne, List<Integer> listTwo) {
    return (listOne == null && listTwo != null) || (listOne != null && listTwo == null) || (listOne.size() != listTwo.size());
}
2
Pavlo Zvarych

En plus de la réponse de Laurence, si vous voulez également le rendre null-safe:

private static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    if (list1 == null)
        return list2==null;
    if (list2 == null)
        return list1 == null;
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}
1
Felixuko

Exemple de code:

public static '<'T'>' boolean isListDifferent(List'<'T'>' previousList,
        List'<'T'>' newList) {

    int sizePrevoisList = -1;
    int sizeNewList = -1;

    if (previousList != null && !previousList.isEmpty()) {
        sizePrevoisList = previousList.size();
    }
    if (newList != null && !newList.isEmpty()) {
        sizeNewList = newList.size();
    }

    if ((sizePrevoisList == -1) && (sizeNewList == -1)) {
        return false;
    }

    if (sizeNewList != sizePrevoisList) {
        return true;
    }

    List n_prevois = new ArrayList(previousList);
    List n_new = new ArrayList(newList);

    try {
        Collections.sort(n_prevois);
        Collections.sort(n_new);
    } catch (ClassCastException exp) {
        return true;
    }

    for (int i = 0; i < sizeNewList; i++) {
        Object obj_prevois = n_prevois.get(i);
        Object obj_new = n_new.get(i);
        if (obj_new.equals(obj_prevois)) {
            // Object are same
        } else {
            return true;
        }
    }

    return false;
}
1
Jaydip Halake

Pour ce faire, la méthode des égaux sur Liste s’effectue. Les listes sont ordonnées. Par conséquent, pour être égales, deux listes doivent comporter les mêmes éléments dans le même ordre.

return list1.equals(list2);
1
daveb
list1.equals(list2);

Si votre liste contient une classe personnalisée MyClass, cette classe doit remplacer la fonction equals.

 class MyClass
  {
  int field=0;
  @0verride
  public boolean equals(Object other)
        {
        if(this==other) return true;
        if(other==null || !(other instanceof MyClass)) return false;
        return this.field== MyClass.class.cast(other).field;
        }
  }

Remarque: si vous souhaitez tester l'égalité sur un Java.util.Set plutôt que sur un Java.util.List, votre objet doit remplacer la fonction hashCode.

1
Pierre

Vous pouvez utiliser la bibliothèque org.Apache.commons.collections d'Apache: http://commons.Apache.org/collections/apidocs/org/Apache/commons/collections/ListUtils.html

public static boolean isEqualList(Java.util.Collection list1,
                              Java.util.Collection list2)
0
David Zhao

Vérifiez que les deux listes ne sont pas nulles. Si leurs tailles sont différentes, ces listes ne sont pas égales. Construisez des cartes contenant les éléments des listes sous forme de clés et leurs répétitions sous forme de valeurs et comparez les cartes.

Hypothèses, si les deux listes sont nulles, je les considère égales.

private boolean compareLists(List<?> l1, List<?> l2) {
    if (l1 == null && l2 == null) {
        return true;
    } else if (l1 == null || l2 == null) {
        return false;
    }

    if (l1.size() != l2.size()) {
        return false;
    }

    Map<?, Integer> m1 = toMap(l1);
    Map<?, Integer> m2 = toMap(l2);

    return m1.equals(m2);
}

private Map<Object, Integer> toMap(List<?> list) {
    //Effective size, not to resize in the future.
    int mapSize = (int) (list.size() / 0.75 + 1);
    Map<Object, Integer> map = new HashMap<>(mapSize);

    for (Object o : list) {
        Integer count = map.get(o);
        if (count == null) {
            map.put(o, 1);
        } else {
            map.put(o, ++count);
        }
    }

    System.out.println(map);
    return map;
}

Veuillez noter que la méthode equals doit être correctement définie pour ces objets . https://stackoverflow.com/a/24814634/4587961

0
Yan Khonski
0
Jeremy Smyth