Comparez deux objets en Java avec des valeurs nulles possibles


201

Je veux comparer deux chaînes pour l'égalité lorsque l'une ou les deux peuvent l'être null.

Donc, je ne peux pas simplement appeler .equals()car il peut contenir des nullvaleurs.

Le code que j'ai essayé jusqu'à présent:

boolean compare(String str1, String str2) {
  return ((str1 == str2) || (str1 != null && str1.equals(str2)));
}

Quelle sera la meilleure façon de vérifier toutes les valeurs possibles, y compris null?



Notez qu'il est un peu déroutant d'appeler la méthode compare, cela a une signification différente pour les chaînes. Vous devriez l'appeler equals.
Florian F

Réponses:


183

Voici ce que le code interne Java utilise (sur d'autres compareméthodes):

public static boolean compare(String str1, String str2) {
    return (str1 == null ? str2 == null : str1.equals(str2));
}

3
Pourquoi ne pas simplement changer les types String en Objects - le rendant plus générique? Et puis c'est la même chose que ce que vous obtiendrez si vous passez à Java 7.
Tom

3
Quelle classe java contient la méthode que vous avez fournie?
Volodymyr Levytskyi

34
Hey! vous ne devriez pas avoir une méthode de comparaison renvoyant vrai / faux. Equals n'est pas la même chose que comparer. Comparer devrait être utile pour le tri. Vous devriez revenir <0, ==0ou >0indiquer lequel est plus bas / plus râpé que l'autre
coya

13
Je suggère d'utiliser Objects.equals(Object, Object)comme Mark Rotteveel l'a souligné dans sa réponse (veuillez voter pour)
Neuron

1
@Eric cela n'en fait pas vraiment moins une bonne réponse. Je suis sûr que vous ne diriez pas que la meilleure réponse est toujours celle contenant uniquement du code compatible avec toutes les versions de java qui ont toujours existé
Neuron

341

Depuis Java 7, vous pouvez utiliser la méthode statique java.util.Objects.equals(Object, Object)pour effectuer des vérifications égales sur deux objets sans vous soucier de leur existence null.

Si les deux objets sont, nullil retournera true, si l'un est nullet l'autre non, il reviendra false. Sinon, il renverra le résultat de l'appel equalsdu premier objet avec le second comme argument.


cette approche diffère la vérification de type jusqu'à l'exécution du programme. deux conséquences: il sera plus lent à l'exécution et l'EDI ne vous fera pas savoir si vous essayez accidentellement de comparer différents types.
averasko

10
@averasko Lorsque vous utilisez, Object.equals(Object)il n'y a pas de vérification de type à la compilation. Cela Objects.equals(Object, Object)fonctionne à peu près de la même manière qu'une equalsinférence / vérifications normale par IDE, ces règles sont des heuristiques qui sont également prises en charge pour Objects.equals(Object, Object)(par exemple, IntelliJ IDEA 2016.2 a le même contrôle pour les égaux normaux et celui des objets).
Mark Rotteveel

2
J'aime cette approche. Pas besoin d'inclure une bibliothèque ApacheCommons pour les choses de nos jours.
Torsten Ojaperv

1
@SimonBaars Cela n'exécute pas une fonction sur un objet nul, c'est une méthode statique que vous passez deux arguments, qui peuvent être null, ou une référence à un objet.
Mark Rotteveel

8
à mon humble avis, cela devrait être la réponse acceptée. Je ne veux pas réinventer la roue dans chaque application que j'écris
Neuron

45

Dans ces cas, il serait préférable d'utiliser Apache Commons StringUtils # equals , il gère déjà les chaînes nulles. Exemple de code:

public boolean compare(String s1, String s2) {
    return StringUtils.equals(s1, s2);
}

Si vous ne souhaitez pas ajouter la bibliothèque, copiez simplement le code source de la StringUtils#equalsméthode et appliquez-le lorsque vous en avez besoin.


1
java.util.Objects est le meilleur depuis Java7, voir autre réponse
tkruse

29

Pour ceux sur Android, qui ne peuvent pas utiliser Objects.equals (str1, str2) de l'API 19, il y a ceci:

android.text.TextUtils.equals(str1, str2);

C'est nul sûr. Il doit rarement utiliser la méthode string.equals () plus chère car les chaînes identiques sur Android se comparent presque toujours à l'opérande "==" grâce au regroupement de chaînes d' Android , et les vérifications de longueur sont un moyen rapide de filtrer la plupart des incompatibilités.

Code source:

/**
 * Returns true if a and b are equal, including if they are both null.
 * <p><i>Note: In platform versions 1.1 and earlier, this method only worked  well if
 * both the arguments were instances of String.</i></p>
 * @param a first CharSequence to check
 * @param b second CharSequence to check
 * @return true if a and b are equal
 */
public static boolean equals(CharSequence a, CharSequence b) {
    if (a == b) return true;
    int length;
    if (a != null && b != null && (length = a.length()) == b.length()) {
        if (a instanceof String && b instanceof String) {
            return a.equals(b);
        } else {
            for (int i = 0; i < length; i++) {
                if (a.charAt(i) != b.charAt(i)) return false;
            }
            return true;
        }
    }
    return false;
}

7

Depuis la version 3.5 Apache Commons StringUtils a les méthodes suivantes:

static int  compare(String str1, String str2)
static int  compare(String str1, String str2, boolean nullIsLess)
static int  compareIgnoreCase(String str1, String str2)
static int  compareIgnoreCase(String str1, String str2, boolean nullIsLess)

Ceux-ci fournissent une comparaison de chaîne sûre nulle.


6

Utilisation de Java 8:

private static Comparator<String> nullSafeStringComparator = Comparator
        .nullsFirst(String::compareToIgnoreCase); 

private static Comparator<Metadata> metadataComparator = Comparator
        .comparing(Metadata::getName, nullSafeStringComparator)
        .thenComparing(Metadata::getValue, nullSafeStringComparator);

public int compareTo(Metadata that) {
    return metadataComparator.compare(this, that);
}

4

Comparez deux chaînes en utilisant les méthodes equals (-, -) et equalsIgnoreCase (-, -) de la classe Apache Commons StringUtils.

StringUtils.equals (-, -) :

StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false

StringUtils.equalsIgnoreCase (-, -) :

StringUtils.equalsIgnoreCase(null, null)   = true
StringUtils.equalsIgnoreCase(null, "abc")  = false
StringUtils.equalsIgnoreCase("xyz", null)  = false
StringUtils.equalsIgnoreCase("xyz", "xyz") = true
StringUtils.equalsIgnoreCase("xyz", "XYZ") = true

3

Vous pouvez utiliser java.util.Objectscomme suit.

public static boolean compare(String str1, String str2) {
    return Objects.equals(str1, str2);
}

2
boolean compare(String str1, String str2) {
    if(str1==null || str2==null) {
        //return false; if you assume null not equal to null
        return str1==str2;
    }
    return str1.equals(str2);
}

est-ce ce que vous vouliez?


5
Ce cas est renvoyé falsesi les deux chaînes ne correspondent nullpeut-être pas au résultat souhaité.
JScoobyCed

1
boolean compare(String str1, String str2) {
    if (str1 == null || str2 == null)
        return str1 == str2;

    return str1.equals(str2);
}

1
boolean compare(String str1, String str2) {
  return (str1==null || str2==null) ? str1 == str2 : str1.equals(str2);
}

0

OK, alors que signifie «meilleure solution possible»?

Si vous voulez dire la plus lisible, alors toutes les solutions possibles sont à peu près équivalentes pour un programmeur Java expérimenté. Mais l'OMI le plus lisible est celui-ci

 public boolean compareStringsOrNulls(String str1, String str2) {
     // Implement it how you like
 }

En d'autres termes, cachez l'implémentation dans une méthode simple qui (idéalement) peut être intégrée.

(Vous pouvez également «out-source» vers une bibliothèque d'utilitaires tiers ... si vous l'utilisez déjà dans votre base de code.)


Si vous voulez dire le plus performant, alors:

  1. la solution la plus performante dépend de la plateforme et du contexte,
  2. l'un des problèmes de «contexte» est la fréquence relative (dynamique) d'occurrence des nullarguments,
  3. il probablement n'a pas d' importance quelle version est plus rapide ... car la différence est probablement trop petit pour faire une différence dans la performance globale de l' application, et
  4. si cela compte, le seul moyen de déterminer lequel est le plus rapide SUR VOTRE PLATEFORME est d'essayer les deux versions et de mesurer la différence.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.