1
votes

Comment vérifier l'égalité de deux collections en ignorant la casse?

Je veux traiter les valeurs minuscules et majuscules de la même manière, et m'assurer que deux collections sont égales (il n'est pas nécessaire de les trier)

Voici mon implémentation:

private boolean stringCollectionEqualsIgnoreCase(Collection<String> c1, Collection<String> c2)
{
    Set<String> s1 = new HashSet<>();
    c1.forEach(i -> s1.add(i.toLowerCase()));

    Set<String> s2 = new HashSet<>();
    c2.forEach(i -> s2.add(i.toLowerCase()));

    return s1.size() == s2.size() && s2.containsAll(s1);
}

Existe-t-il un moyen plus simple de le faire? Ou vaut-il mieux avoir ma propre méthode


10 commentaires

Cela traitera Arrays.asList ("a") comme égal à Arrays.asList ("a", "a") , est-ce ce que vous voulez?


@OpenSauce c'est très bien. Avant de passer dans la collection, j'ai Set uniqueStrings = new HashSet <> (strings);


dans ce cas, ce que vous faites devrait bien se passer. La méthode equals pour AbstractSet fait fondamentalement la même chose. En fait, vous pouvez changer votre dernière ligne en s1.equals (s2) .


Vous allez bien.


@davidxxx J'allais rédiger une réponse, mais cela aurait été fondamentalement équivalent à la réponse que vous avez postée puis supprimée. Peut-être pourriez-vous annuler la suppression de votre réponse.


@StuartMarks, donc, équivalent à ma réponse? :)


@MickMnemonic Conceptuellement oui, mais davidxxx a plus de détails sur le code exact qui devrait être écrit.


@Stuart Marks non supprimés. Cela ne semblait pas être une solution efficace en termes de performances. J'ai donc préféré supprimer. Qu'est ce que tu penses de ça ?


J'ai compris d'après le commentaire d'OP ci-dessus qu'ils travaillaient avec un Set pour commencer, donc toute la méthode utilitaire, une copie d'éléments, semblait redondante; ils pourraient simplement utiliser les TreeMap dès le début.


@davidxxx En supposant que l'OP n'est pas concerné par la commande ou les doublons, le code de la question est raisonnable et fonctionnera probablement la plupart du temps. Mais notez que la dernière ligne (qui équivaut à s1.equals (s2) ) utilise equals () pour comparer les chaînes en minuscules. Ceci est légèrement différent de String.equalsIgnoreCase () .


3 Réponses :


0
votes

Puisqu'il n'y a pas de bibliothèque native fournissant cela, je pense que vous devrez toujours faire une solution de contournement, mais ce qui suit pourrait être une solution élégante sans avoir à modifier vos collections existantes.

public List<String> toLowerCaseList(Collection<String> var) {
    return var.stream().map(String::toLowerCase).toList();
}

public boolean containsAllIgnoreCase(Collection<String> var1, Collection<String> var2) {
    return toLowerCaseList(var1).containsAll(toLowerCaseList(var2));
}

Ou encore plus facile à lire

public boolean containsAllIgnoreCase(Collection<String> var1, Collection<String> var2) {
    return var1.stream()
               .map(String::toLowerCase)
               .collect(toList())
               .containsAll(var2.stream()
                                .map(String::toLowerCase)
                                .collect(toList());
}


0 commentaires

2
votes

Votre solution actuelle est tout à fait acceptable. C'est assez clair et il devrait avoir une bonne performance globale.

Vous devez utiliser Set.equals () au lieu de Set.containsAll () . Cela fait les mêmes choses mais cela vous évite de comparer la taille comme optimisation.

1) Une version stream de votre code pourrait être:

private boolean stringCollectionEqualsIgnoreCase(Collection<String> c1, Collection<String> c2)
{
    Comparator<String> comp = Comparator.comparing(String::toLowerCase);
    Set<String> s1 = new TreeSet<>(comp);
    Set<String> s2 = new TreeSet<>(comp);
    s1.addAll(c1);
    s2.addAll(c2);
    return s1.equals(s2);
}

2) Voici une deuxième alternative avec Treeset .
Je ne suis pas sûr que ce soit plus facile mais cela évite les boucles explicites et rend la logique plus explicite car factorisée:

private boolean stringCollectionEqualsIgnoreCase(Collection<String> c1, Collection<String> c2) {
    return c1.stream()
             .map(String::toLowerCase)
             .collect(toSet())
             .equals(c2.stream()
                       .map(String::toLowerCase)
                       .collect(toSet()));
}

Notez que cela supprime les doublons selon le comparateur. Cela signifie que ça trie. Cela pourrait donc être plus lent ou plus rapide que votre solution réelle selon les cas. Bien sûr, pour les petits Set à comparer, cela n'a pas d'importance.


3 commentaires

+1 pour l'approche TreeSet utilisant String.CASE_INSENSITIVE_ORDER . Ce comparateur correspond à String.compareToIgnoreCase () qui, lorsqu'il renvoie zéro, doit être cohérent avec String.equalsIgnoreCase () . Notez que la sémantique fournie ici est quelque peu différente du code affiché dans la question, qui en fait s1.toLowerCase (). Equals (s2.toLowerCase ()) . Notez que l'approche de comparaison n'est pas sensible à la locale alors que l'approche toLowerCase () dépend implicitement de la locale par défaut. On ne sait pas si cela est important pour le PO, mais cela pourrait donner des résultats inattendus.


Si une approche insensible à la casse et spécifique aux paramètres régionaux est requise, une instance Collator appropriée peut être utilisée comme comparateur TreeSet au lieu de String.CASE_INSENSITIVE_ORDER. Cette approche ne peut pas être utilisée avec toLowerCase () et HashSets. L'approche TreeSet est probablement plus lente que l'approche HashSet, donc cela dépend si l'OP est plus préoccupé par la sémantique flexible ou les performances.


@Stuart Marks En effet, il y a cette différence subtile que je n'ai pas remarquée. Donc oui Collator pour des paramètres régionaux spécifiques (qui n'est pas nécessairement celui par défaut) et Comparator.comparing (String :: toLowerCase) si les paramètres régionaux par défaut conviennent.



0
votes

Si une implémentation qui fonctionne avec les Set s suffit, vous pouvez stocker vos éléments uniques dans un TreeSet avec un comparateur spécial insensible à la casse:

Set<String> uniqueStrings = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);


0 commentaires