equals vs Arrays.equals em Java

209

Ao comparar matrizes em Java, existem diferenças entre as 2 instruções a seguir?

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

E se sim, o que são?

PandaConda
fonte
Dê uma olhada também em java.util.Arrays.deepEquals (Object [] a1, Object [] a2)
ultra-

Respostas:

299

array1.equals(array2)é o mesmo que array1 == array2, ou seja, é a mesma matriz. Como o @alf aponta, não é o que a maioria das pessoas espera.

Arrays.equals(array1, array2) compara o conteúdo das matrizes.


Da mesma forma, array.toString()pode não ser muito útil e você precisa usá-lo Arrays.toString(array).

Peter Lawrey
fonte
59
Observe que Arrays.equals()não funciona conforme o esperado para matrizes multidimensionais, ele apenas compara itens da 1ª dimensão para igualdade de referência. O Apache commons ArrayUtils.isEqualstrabalha com matrizes multidimensionais.
Adam Parkin
4
Estou atordoado. Existe uma razão para que array.equals seja implementado na comparação de ponteiros, em vez de comparar comprimento e cada objeto?
Lake
2
O @Lake compara o comprimento da matriz e os objetos contidos, mas o que ele não faz é uma comparação profunda. O fato de igual funcionar como esperado para matrizes está quebrado, isso não deve ser um problema em primeiro lugar.
Peter Lawrey
48
@AdamParkin É por isso que temos Arrays.deepEquals(Object[], Object[]).
Elliott Frisch
3
@JeewanthaSamaraweera, que é a definição para esse método, no entanto .equals, não compara o conteúdo e é por isso que você precisa desse método.
Peter Lawrey
86

É um problema infame: as .equals()matrizes estão muito danificadas, mas nunca a usem.

Dito isto, não está "quebrado", como em "alguém fez isso de uma maneira realmente errada" - está apenas fazendo o que está definido e não o que normalmente é esperado. Então, para os puristas: está perfeitamente bem, e isso também significa, nunca use.

Agora, o comportamento esperado para equalsé comparar dados. O comportamento padrão é comparar a identidade, comoObject não possui nenhum dado (para puristas: sim, mas não é esse o ponto); suposição é que, se você precisar equalsde subclasses, você o implementará. Nas matrizes, não há implementação para você, portanto você não deve usá-la.

Portanto, a diferença é que Arrays.equals(array1, array2)funciona como seria de esperar (ou seja, compara o conteúdo), array1.equals(array2)volta à Object.equalsimplementação, que por sua vez compara a identidade e, portanto, é melhor substituída por ==(para puristas: sim, eu sei sobrenull ).

O problema é que mesmo Arrays.equals(array1, array2)o morderá com força se elementos da matriz não implementaremequals corretamente. É uma afirmação muito ingênua, eu sei, mas há um caso menos importante do que óbvio: considere uma matriz 2D.

A matriz 2D em Java é uma matriz de matrizes, e as matrizes ' equalsestão quebradas (ou inúteis, se você preferir); portanto Arrays.equals(array1, array2), não funcionará como esperado em matrizes 2D.

Espero que ajude.

alf
fonte
13
Não está quebrado, apenas herdado do Object.
Michael Borgwardt
Uma matriz possui uma implementação personalizada equals()? Eu pensei que não era substituído pelo objeto.
Martijn Courteaux
@MichaelBorgwardt é uma biblioteca de sistema, com um método que não faz o que é dito no javadoc. Parece quebrado o suficiente para mim. Dito isto, admito que é uma afirmação muito discutível, mas acredito que "está quebrado" é lembrado melhor e, portanto, é muito mais conveniente pensar dessa maneira.
alf
@MartijnCourteaux que é exatamente o problema :)
alf
3
Para matrizes de matrizes, você precisa Arrays.deepEquals--- é o que someArray.equalsdeveria ter feito o tempo todo. (Relacionado:. Objects.deepEquals)
Kevin J. Chase
16

Examine a implementação dos dois métodos para entendê-los profundamente:

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

enquanto:

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
fonte
11

Suspiro. Nos anos 70, eu era o "programador de sistemas" (sysadmin) de um sistema IBM 370, e meu empregador era membro do grupo de usuários IBM SHARE. Às vezes, acontecia que alguém enviasse um APAR (relatório de bug) sobre algum comportamento inesperado de algum comando do CMS, e a IBM respondia NOTABUG: o comando faz o que foi projetado para fazer (e o que a documentação diz).

A SHARE veio com um contador para isso: BAD - Broken As Designed. Eu acho que isso pode se aplicar a esta implementação de iguais para matrizes.

Não há nada errado com a implementação do Object.equals. O objeto não possui membros de dados, portanto, não há nada para comparar. Dois "Objetos" são iguais se, e somente se, são, de fato, o mesmo Objeto (internamente, o mesmo endereço e comprimento).

Mas essa lógica não se aplica a matrizes. As matrizes têm dados e você espera que a comparação (via igual) compare os dados. Idealmente, da mesma forma que Arrays.deepEquals, mas pelo menos da mesma maneira que Arrays.equals (comparação superficial dos elementos).

Portanto, o problema é que a matriz (como um objeto interno) não substitui Object.equals. String (como uma classe chamada) faz override Object.equals e dar o resultado que você espera.

Outras respostas dadas estão corretas: [...]. Equals ([....]) simplesmente compara os ponteiros e não o conteúdo. Talvez um dia alguém corrija isso. Ou talvez não: quantos programas existentes quebrariam se [...] igualasse realmente comparasse os elementos? Desconfio que não muitos, mas mais que zero.

AP Damien
fonte
5
Eu gosto do acrônimo Broken.As.Designed
Chris
5

Arrays herdam equals()a partir Objecte, portanto, comparar só retorna verdadeiro se comparar uma ordem contra si.

Por outro lado, Arrays.equalscompara os elementos das matrizes.

Este trecho elucida a diferença:

Object o1 = new Object();
Object o2 = new Object();
Object[] a1 = { o1, o2 };
Object[] a2 = { o1, o2 };
System.out.println(a1.equals(a2)); // prints false
System.out.println(Arrays.equals(a1, a2)); // prints true

Veja também Arrays.equals(). Outro método estático também pode ser de interesse: Arrays.deepEquals().

Adam Zalcman
fonte
1

O Arrays.equals(array1, array2):

verifique se ambas as matrizes contêm o mesmo número de elementos e se todos os pares correspondentes de elementos nas duas matrizes são iguais.

O array1.equals(array2):

compare o objeto com outro objeto e retorne true somente se a referência dos dois objetos for igual à do Object.equals()

aleroot
fonte
0

A equals()matriz de é herdada de Object, portanto, ela não olha para o conteúdo das matrizes, apenas considera cada matriz igual a si mesma.

Os Arrays.equals()métodos que comparar o conteúdo das matrizes. Há sobrecargas para todos os tipos primitivos, e a de objetos usa os equals()métodos dos próprios objetos .

Michael Borgwardt
fonte
2
você diz "conteúdo de matrizes", isso significa matrizes multidimensionais também?
AlanFoster
@AlanFoster: não. As matrizes multidimensionais são matrizes de matrizes, o que significa que eles método Arrays.equals (Object [], Object []) irá ser invocada, que chama é igual a das sub-matrizes () métodos
Michael Borgwardt
0
import java.util.Arrays;
public class ArrayDemo {
   public static void main(String[] args) {
   // initializing three object arrays
   Object[] array1 = new Object[] { 1, 123 };
   Object[] array2 = new Object[] { 1, 123, 22, 4 };
   Object[] array3 = new Object[] { 1, 123 };

   // comparing array1 and array2
   boolean retval=Arrays.equals(array1, array2);
   System.out.println("array1 and array2 equal: " + retval);
   System.out.println("array1 and array2 equal: " + array1.equals(array2));

   // comparing array1 and array3
   boolean retval2=Arrays.equals(array1, array3);
   System.out.println("array1 and array3 equal: " + retval2);
   System.out.println("array1 and array3 equal: " + array1.equals(array3));

   }
}

Aqui está a saída:

    array1 and array2 equal: false
    array1 and array2 equal: false

    array1 and array3 equal: true
    array1 and array3 equal: false

Vendo esse tipo de problema, eu procuraria pessoalmente Arrays.equals(array1, array2), de acordo com sua pergunta, para evitar confusão.

Tayyab
fonte
Parece estar correto, mas em matrizes, a ordem dos elementos também é importante. Por exemplo, se houver outra matriz Object [] array4 = new Object [] {123, 1}; com Arrays.equals (array3, array4), ele retornará false.
Jiahao