175 votes

Java ArrayList - comment puis-je savoir si deux listes sont égales, l'ordre n'ayant pas d'importance ?

J'ai deux ArrayList de type Answer (classe autodidacte).

J'aimerais comparer les deux listes pour voir si elles contiennent le même contenu, mais sans que l'ordre ne compte.

Exemple :

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

List.equals stipule que deux listes sont égales si elles contiennent la même taille, le même contenu et le même ordre d'éléments. Je veux la même chose, mais sans que l'ordre importe.

Existe-t-il un moyen simple de le faire ? Ou dois-je faire une boucle for imbriquée, et vérifier manuellement chaque index des deux listes ?

Note : Je ne peux pas les changer de ArrayList à un autre type de liste, ils doivent le rester.

183voto

Le moyen le plus simple pour tout la liste serait :

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

143voto

jschoen Points 5721

Vous pouvez trier les deux listes en utilisant Collections.sort() puis utiliser la méthode des égalités. Une solution légèrement meilleure consiste à vérifier d'abord si elles ont la même longueur avant de les ordonner, si ce n'est pas le cas, alors elles ne sont pas égales, puis à les trier, puis à utiliser la méthode equals. Par exemple, si vous aviez deux listes de chaînes de caractères, cela donnerait quelque chose comme.. :

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

110voto

acdcjunior Points 19898

Apache Commons Les collections à la rescousse, une fois de plus :

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)

Renvoie à true si les Collection contiennent exactement les mêmes éléments avec exactement les mêmes cardinalités.

C'est-à-dire que si le cardinalité de e sur a est égale à la cardinalité de e sur b pour chaque élément e sur a ou b .

Paramètres :

  • a - la première collection, ne doit pas être null
  • b - le deuxième collection, ne doit pas être null

Les retours : true si les collections contiennent les mêmes éléments avec les mêmes cardinalités.

17voto

cHao Points 42294
// 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;
}

14voto

mike rodent Points 482

Je dirais que ces réponses manquent un truc.

Bloch, dans son ouvrage essentiel, merveilleux, concis Java efficace Dans son article 47, intitulé "Connaître et utiliser les bibliothèques", il déclare : "En résumé, ne réinventez pas la roue". Et il donne plusieurs raisons très claires pour ne pas le faire.

Il y a quelques réponses ici qui suggèrent des méthodes de CollectionUtils dans la bibliothèque Apache Commons Collections mais aucun n'a repéré la manière la plus belle et la plus élégante de répondre à cette question :

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

}

Coupables : c'est-à-dire les éléments qui ne sont pas communs aux deux Lists . Déterminer quels coupables appartiennent à list1 et à laquelle list2 est relativement simple en utilisant CollectionUtils.intersection( list1, culprits ) y CollectionUtils.intersection( list2, culprits ) .
Cependant, il a tendance à s'effondrer dans des cas comme { "a", "a", "b" }. disjunction avec { "a", "b", "b" } ... sauf que ce n'est pas un défaut du logiciel, mais que c'est inhérent à la nature des subtilités/ambiguïtés de la tâche souhaitée.

Vous pouvez toujours examiner le code source (l. 287) pour une tâche comme celle-ci, tel que produit par les ingénieurs d'Apache. L'un des avantages de l'utilisation de leur code est qu'il a été minutieusement testé, et que de nombreux cas limites et problèmes ont été anticipés et résolus. Vous pouvez copier et modifier ce code à votre guise si nécessaire.


NB J'ai d'abord été déçu qu'aucune des CollectionUtils fournit une version surchargée vous permettant d'imposer votre propre Comparator (vous pouvez donc redéfinir equals selon vos besoins).

Mais à partir de collections4 4.0, il y a une nouvelle classe, Equator qui "détermine l'égalité entre les objets de type T". En examinant le code source de collections4 CollectionUtils.java, il semble qu'ils l'utilisent avec certaines méthodes, mais pour autant que je puisse en juger, cela ne s'applique pas aux méthodes situées en haut du fichier, qui utilisent l'attribut CardinalityHelper qui comprennent disjunction y intersection .

Je suppose que les gens d'Apache ne se sont pas encore penchés sur cette question parce qu'elle n'est pas triviale : il faudrait créer quelque chose comme une classe "AbstractEquatingCollection" qui, au lieu d'utiliser les caractéristiques inhérentes de ses éléments, utiliserait la méthode de calcul de l'équation. equals y hashCode devraient plutôt utiliser celles de Equator pour toutes les méthodes de base, telles que add , contains etc. NB en fait quand on regarde le code source, AbstractCollection ne met pas en œuvre add ni ses sous-classes abstraites telles que AbstractSet ... vous devez attendre jusqu'à ce que les classes concrètes telles que HashSet y ArrayList avant add est mis en œuvre. C'est un véritable casse-tête.

En attendant, surveillez cet espace, je suppose. La solution provisoire évidente serait d'envelopper tous vos éléments dans une classe d'enveloppe sur mesure qui utilise equals y hashCode pour mettre en œuvre le type d'égalité que vous voulez... puis manipuler Collections de ces objets enveloppes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X