web-dev-qa-db-fra.com

Comment comparer l'égalité des listes de tableaux avec Java moderne?

J'ai deux listes de tableaux.

Comment comparer facilement l'égalité de ceux-ci avec Java 8 et ses fonctionnalités , sans utiliser de bibliothèques externes? Je cherche pour une "meilleure" solution (de niveau supérieur, plus courte, plus efficace) que le code de force brute comme celui-ci (le code non testé, peut contenir des fautes de frappe, etc., pas le point de la question):

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

Ou, s'il n'y a pas de meilleur moyen, c'est une réponse valable aussi.

Bonus: si Java 9 offre un meilleur moyen que ce que whaterver Java 8 peut offrir, n'hésitez pas à le mentionner également).

Edit: Après avoir regardé les commentaires, et vu comment cette question est devenue modérément chaude, je pense le " mieux "devrait inclure la première vérification de la longueur de tous les tableaux, avant de vérifier le contenu du tablea, car cela a le potentiel de trouver l'inégalité beaucoup plus rapidement, si les tableaux internes sont longs.

68
hyde

1) Solution basée sur Java 8 flux:

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) Solution beaucoup plus simple (fonctionne en Java 5+):

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

) Concernant votre nouvelle exigence (pour vérifier d'abord la longueur des tableaux String contenus), vous pouvez écrire une méthode d'assistance générique qui vérifie l'égalité des listes transformées:

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

La solution pourrait alors être:

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

La boucle for au moins peut être rationalisée, conduisant à:

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

en utilisant la fonction Zip (qui provient de lambda b93) de https://stackoverflow.com/a/23529010/75518 , le code pourrait ressembler à:

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

mise à jour

afin de vérifier d'abord la taille des tableaux, puis le contenu, cela pourrait être une solution à envisager

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

Vous pourriez utiliser un flux si les listes sont des listes à accès aléatoire (de sorte qu'un appel à get est rapide - temps généralement constant) conduisant à:

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

Cependant, vous pouvez donner comme paramètres certaines implémentations qui ne le sont pas (telles que LinkedLists). Dans ce cas, la meilleure façon est d'utiliser explicitement l'itérateur. Quelque chose comme:

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.

Vous pouvez diffuser sur une liste et comparer à chaque élément de l'autre en utilisant un itérateur:

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

Il est préférable d'utiliser un itérateur au lieu de la méthode get(index) sur la première liste car peu importe que la liste soit RandomAccess ou non.

Remarque: cela ne fonctionne qu'avec un flux séquentiel . L'utilisation d'un flux parallèle entraînera de mauvais résultats.


EDIT: Selon la dernière édition de la question, qui indique qu'il serait préférable de vérifier la longueur de chaque paire de tableaux à l'avance , je pense que cela pourrait être réalisé avec une légère modification de mon code précédent:

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

Ici, j'utilise deux itérateurs et je diffuse list2 Deux fois, mais je ne vois aucun autre moyen de vérifier toutes les longueurs avant de vérifier le contenu de la première paire de tableaux. La vérification des longueurs est null-safe, tandis que la vérification du contenu est déléguée à la méthode Arrays.equals(array1, array2) .