Compare dos objetos en Java con posibles valores nulos

189

Quiero comparar dos cadenas para la igualdad en Java, cuando uno o ambos podrían ser null, por lo que no puedo simplemente llamar .equals(). ¿Cuál es la mejor manera?

boolean compare(String str1, String str2) {
    ...
}

Editar:

return ((str1 == str2) || (str1 != null && str1.equals(str2)));
priceline
fuente

Respuestas:

173

Esto es lo que usa el código interno de Java (en otros comparemétodos):

public static boolean compare(String str1, String str2) {
    return (str1 == null ? str2 == null : str1.equals(str2));
}
Eric
fuente
3
¿Por qué no simplemente cambiar los tipos de cadena a objetos, haciéndolo más genérico? Y luego es lo mismo que obtendrás si te mudas a Java 7.
Tom
3
¿Qué clase de Java contiene el método que proporcionó?
Volodymyr Levytskyi
30
¡Oye! No debería tener un método de comparación que devuelva verdadero / falso. Igual no es lo mismo que comparar. Comparar debería ser útil para ordenar. Debe devolver <0, ==0o >0para indicar cuál es menor / rallador que el otro
Coya
10
sugiero usar Objects.equals(Object, Object)como Mark Rotteveel ha señalado en su respuesta (favor de votar)
Neuron
1
@Eric eso realmente no lo hace menos una buena respuesta. Estoy seguro de que no argumentaría que la mejor respuesta es siempre la que contiene solo el código que es compatible con todas las versiones de Java que siempre ha existido
Neuron
318

Desde Java 7, puede usar el método estático java.util.Objects.equals(Object, Object)para realizar verificaciones iguales en dos objetos sin preocuparse por ellos null.

Si ambos objetos son null, volverá true, si uno es nully otro no, volverá false. De lo contrario, devolverá el resultado de invocar equalsel primer objeto con el segundo como argumento.

Mark Rotteveel
fuente
Este enfoque difiere la verificación de tipo hasta el tiempo de ejecución del programa. dos consecuencias: será más lento en tiempo de ejecución e IDE no le avisará si por accidente intenta comparar diferentes tipos.
averasko
10
@averasko Cuando lo usa, Object.equals(Object)no hay verificación de tipo de tiempo de compilación. El Objects.equals(Object, Object)funcionamiento es muy lo mismo que una normal, equalsy vuelva a la inferencia / controles por IDE, esas reglas heurísticas que son también son compatibles para Objects.equals(Object, Object)(por ejemplo, IntelliJ IDEA 2.016,2 tiene el mismo cheque, tanto para los iguales normales y la de los objetos).
Mark Rotteveel
2
Me gusta este enfoque. No es necesario incluir alguna biblioteca ApacheCommons para las cosas en estos días.
Torsten Ojaperv
1
@SimonBaars Esto no ejecuta una función en un objeto nulo, este es un método estático al que le pasa dos argumentos, que pueden ser nulos, o una referencia a un objeto.
Mark Rotteveel
8
En mi opinión, esta debería ser la respuesta aceptada. No quiero reinventar la rueda en cada aplicación que escribo
Neuron
45

Para estos casos, sería mejor usar Apache Commons StringUtils # igual , ya maneja cadenas nulas. Código de muestra:

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

Si no desea agregar la biblioteca, simplemente copie el código fuente del StringUtils#equalsmétodo y aplíquelo cuando lo necesite.

Luiggi Mendoza
fuente
29

Para aquellos en Android, que no pueden usar Object.equals (str1, str2) de API 19, existe esto:

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

Es nulo seguro. Raramente tiene que usar el método string.equals () más caro porque las cadenas idénticas en Android casi siempre se comparan con el operando "==" gracias a la Agrupación de cadenas de Android , y las comprobaciones de longitud son una forma rápida de filtrar la mayoría de los desajustes.

Código fuente:

/**
 * 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;
}
NameSpace
fuente
7

Desde la versión 3.5 Apache Commons StringUtils tiene los siguientes métodos:

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)

Estos proporcionan una comparación de cadenas segura nula.

phn
fuente
6

Usando 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);
}
TanvirChowdhury
fuente
4

Compare dos cadenas usando el método equals (-, -) y equalsIgnoreCase (-, -) de la clase 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
Prashant Sahoo
fuente
3

Puedes usar java.util.Objectslo siguiente.

public static boolean compare(String str1, String str2) {
    return Objects.equals(str1, str2);
}
Jobi Subramunian
fuente
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);
}

¿Es esto lo que deseabas?

didxga
fuente
55
Este caso se devuelve falsesi ambas cadenas son las nullque podrían no ser el resultado deseado.
JScoobyCed
1
boolean compare(String str1, String str2) {
    if (str1 == null || str2 == null)
        return str1 == str2;

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

Bien, entonces, ¿qué significa "la mejor solución posible"?

Si te refieres a lo más legible, entonces todas las soluciones posibles son prácticamente equivalentes para un programador Java experimentado. Pero en mi opinión, lo más legible es esto

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

En otras palabras, oculte la implementación dentro de un método simple que (idealmente) se pueda incorporar.

(También puede "externalizar" a una biblioteca de utilidades de terceros ... si ya la usa en su base de código).


Si te refieres al más eficiente, entonces:

  1. la solución más eficiente depende de la plataforma y el contexto,
  2. Una de las cuestiones de "contexto" es la frecuencia relativa (dinámica) de aparición de nullargumentos,
  3. que , probablemente, no importa qué versión es más rápido ... porque la diferencia es probablemente demasiado pequeño para hacer una diferencia en el rendimiento general de la aplicación, y
  4. Si es importante, la única forma de descubrir cuál es el más rápido EN SU PLATAFORMA es probar ambas versiones y medir la diferencia.
Stephen C
fuente