es igual a Arrays.equals en Java

Resuelto PandaConda asked hace 13 años • 9 respuestas

Al comparar matrices en Java, ¿existen diferencias entre las dos declaraciones siguientes?

Object[] array1, array2;
array1.equals(array2);
Arrays.equals(array1, array2);

Y si es así, ¿cuáles son?

PandaConda avatar Jan 08 '12 18:01 PandaConda
Aceptado

array1.equals(array2)es lo mismo que array1 == array2, es decir, es la misma matriz. Como señala @alf, no es lo que la mayoría de la gente espera.

Arrays.equals(array1, array2)compara el contenido de las matrices.


Del mismo modo, array.toString()puede que no sea muy útil y necesites usarlo Arrays.toString(array).

Peter Lawrey avatar Jan 08 '2012 11:01 Peter Lawrey

Es un problema infame: .equals()las matrices están muy rotas, simplemente no las uses nunca.

Dicho esto, no está "roto" como en el caso de "alguien lo ha hecho de una manera realmente incorrecta", sino que simplemente hace lo que está definido y no lo que normalmente se espera. Entonces, para los puristas: está perfectamente bien, y eso también significa que no lo uses nunca.

Ahora el comportamiento esperado equalses comparar datos. El comportamiento predeterminado es comparar la identidad, ya que Objectno tiene ningún dato (para los puristas: sí los tiene, pero ese no es el punto); La suposición es que, si necesita equalssubclases, las implementará. En las matrices, no hay ninguna implementación para usted, por lo que se supone que no debe usarla.

Entonces, la diferencia es Arrays.equals(array1, array2)que funciona como era de esperar (es decir, compara contenido), array1.equals(array2)recurre a Object.equalsla implementación, que a su vez compara la identidad y, por lo tanto, es mejor reemplazarla por ==(para los puristas: sí, lo sé null).

El problema es que incluso Arrays.equals(array1, array2)te morderá fuerte si los elementos de la matriz no se implementan equalscorrectamente. Es una afirmación muy ingenua, lo sé, pero hay un caso muy importante y menos que obvio: considere una matriz 2D.

La matriz 2D en Java es una matriz de matrices, y las matrices equalsestán rotas (o son inútiles si lo prefiere), por lo que Arrays.equals(array1, array2)no funcionará como se espera en matrices 2D.

alf avatar Jan 08 '2012 11:01 alf

Mire dentro de la implementación de los dos métodos para comprenderlos en profundidad:

array1.equals(array2);
/**
 * Indicates whether some other object is "equal to" this one.
 * <p>
 * The {@code equals} method implements an equivalence relation
 * on non-null object references:
 * <ul>
 * <li>It is <i>reflexive</i>: for any non-null reference value
 *     {@code x}, {@code x.equals(x)} should return
 *     {@code true}.
 * <li>It is <i>symmetric</i>: for any non-null reference values
 *     {@code x} and {@code y}, {@code x.equals(y)}
 *     should return {@code true} if and only if
 *     {@code y.equals(x)} returns {@code true}.
 * <li>It is <i>transitive</i>: for any non-null reference values
 *     {@code x}, {@code y}, and {@code z}, if
 *     {@code x.equals(y)} returns {@code true} and
 *     {@code y.equals(z)} returns {@code true}, then
 *     {@code x.equals(z)} should return {@code true}.
 * <li>It is <i>consistent</i>: for any non-null reference values
 *     {@code x} and {@code y}, multiple invocations of
 *     {@code x.equals(y)} consistently return {@code true}
 *     or consistently return {@code false}, provided no
 *     information used in {@code equals} comparisons on the
 *     objects is modified.
 * <li>For any non-null reference value {@code x},
 *     {@code x.equals(null)} should return {@code false}.
 * </ul>
 * <p>
 * The {@code equals} method for class {@code Object} implements
 * the most discriminating possible equivalence relation on objects;
 * that is, for any non-null reference values {@code x} and
 * {@code y}, this method returns {@code true} if and only
 * if {@code x} and {@code y} refer to the same object
 * ({@code x == y} has the value {@code true}).
 * <p>
 * Note that it is generally necessary to override the {@code hashCode}
 * method whenever this method is overridden, so as to maintain the
 * general contract for the {@code hashCode} method, which states
 * that equal objects must have equal hash codes.
 *
 * @param   obj   the reference object with which to compare.
 * @return  {@code true} if this object is the same as the obj
 *          argument; {@code false} otherwise.
 * @see     #hashCode()
 * @see     java.util.HashMap
 */
public boolean equals(Object obj) {
    return (this == obj);
}

mientras:

Arrays.equals(array1, array2);
/**
 * Returns <tt>true</tt> if the two specified arrays of Objects are
 * <i>equal</i> to one another.  The two arrays are considered equal if
 * both arrays contain the same number of elements, and all corresponding
 * pairs of elements in the two arrays are equal.  Two objects <tt>e1</tt>
 * and <tt>e2</tt> are considered <i>equal</i> if <tt>(e1==null ? e2==null
 * : e1.equals(e2))</tt>.  In other words, the two arrays are equal if
 * they contain the same elements in the same order.  Also, two array
 * references are considered equal if both are <tt>null</tt>.<p>
 *
 * @param a one array to be tested for equality
 * @param a2 the other array to be tested for equality
 * @return <tt>true</tt> if the two arrays are equal
 */
public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

    for (int i=0; i<length; i++) {
        Object o1 = a[i];
        Object o2 = a2[i];
        if (!(o1==null ? o2==null : o1.equals(o2)))
            return false;
    }

    return true;
}
Eng.Fouad avatar Jan 08 '2012 12:01 Eng.Fouad

Suspiro. En los años 70 yo era el "programador de sistemas" (administrador de sistemas) de un sistema IBM 370 y mi empleador era miembro del grupo de usuarios de IBM SHARE. A veces sucedía que alguien enviaba un APAR (informe de error) sobre algún comportamiento inesperado de algún comando CMS, y IBM respondía NOTABUG: el comando hace lo que fue diseñado para hacer (y lo que dice la documentación).

A SHARE se le ocurrió una respuesta a esto: MALO: roto según lo diseñado. Creo que esto podría aplicarse a esta implementación de iguales para matrices.

No hay nada de malo en la implementación de Object.equals. El objeto no tiene miembros de datos, por lo que no hay nada que comparar. Dos "Objetos" son iguales si y sólo si son, de hecho, el mismo Objeto (internamente, la misma dirección y longitud).

Pero esa lógica no se aplica a las matrices. Las matrices tienen datos y usted espera que la comparación (a través de iguales) compare los datos. Idealmente, como lo hace Arrays.deepEquals, pero al menos como lo hace Arrays.equals (comparación superficial de los elementos).

Entonces el problema es que la matriz (como objeto incorporado) no anula Object.equals. String (como clase con nombre) anula Object.equals y proporciona el resultado esperado.

Otras respuestas dadas son correctas: [...].equals([....]) simplemente compara los punteros y no los contenidos. Quizás algún día alguien corrija esto. O tal vez no: ¿cuántos programas existentes se estropearían si [...].equals realmente comparara los elementos? Sospecho que no muchos, pero más de cero.

A. P. Damien avatar Apr 29 '2016 16:04 A. P. Damien