entspricht vs Arrays.equals in Java

209

Gibt es beim Vergleich von Arrays in Java Unterschiede zwischen den folgenden beiden Anweisungen?

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

Und wenn ja, was sind sie?

PandaConda
quelle
Werfen

Antworten:

299

array1.equals(array2)ist das gleiche wie array1 == array2, dh es ist das gleiche Array. Wie @alf betont, ist es nicht das, was die meisten Leute erwarten.

Arrays.equals(array1, array2) vergleicht den Inhalt der Arrays.


Ebenso array.toString()kann nicht sehr nützlich sein und Sie müssen verwenden Arrays.toString(array).

Peter Lawrey
quelle
59
Beachten Sie, dass Arrays.equals()dies für mehrdimensionale Arrays nicht wie erwartet funktioniert, sondern nur Elemente der 1. Dimension auf Referenzgleichheit vergleicht. Apache Commons ArrayUtils.isEqualsarbeitet mit mehrdimensionalen Arrays.
Adam Parkin
4
Ich bin beeindruckt. Gibt es einen Grund dafür, dass array.equals für den Zeigervergleich implementiert wird, anstatt die Länge und jedes Objekt zu vergleichen?
See
2
@Lake is vergleicht zwar die Array-Länge und die enthaltenen Objekte, aber was es nicht tut, ist ein tiefer Vergleich. Die Tatsache, dass gleich wie erwartet für Arrays funktioniert, ist defekt, dies sollte überhaupt kein Problem sein.
Peter Lawrey
48
@AdamParkin Deshalb haben wir Arrays.deepEquals(Object[], Object[]).
Elliott Frisch
3
@JeewanthaSamaraweera, das ist die Definition für diese Methode, aber .equalses vergleicht keine Inhalte, weshalb Sie diese Methode benötigen.
Peter Lawrey
86

Es ist ein berüchtigtes Problem: .equals()Da Arrays stark beschädigt sind, verwenden Sie sie niemals.

Das heißt, es ist nicht "kaputt" wie in "jemand hat es wirklich falsch gemacht" - es macht nur das, was definiert ist und nicht das, was normalerweise erwartet wird. Also für Puristen: Es ist vollkommen in Ordnung, und das bedeutet auch, es niemals zu benutzen.

Das erwartete Verhalten für equalsbesteht nun darin, Daten zu vergleichen. Das Standardverhalten besteht darin, die Identität zu vergleichen, da Objectkeine Daten vorhanden sind (für Puristen: Ja, aber es geht nicht darum). Annahme ist, wenn Sie equalsin Unterklassen benötigen , werden Sie es implementieren. In Arrays gibt es keine Implementierung für Sie, daher sollten Sie sie nicht verwenden.

Der Unterschied ist also, Arrays.equals(array1, array2)funktioniert wie erwartet (dh vergleicht Inhalt), array1.equals(array2)fällt zurück Object.equalsImplementierung, was wiederum Identität vergleicht, und somit eine bessere ersetzt durch ==(für Puristen: ja ich kenne null).

Das Problem ist, dass Sie sogar Arrays.equals(array1, array2)hart gebissen werden, wenn Elemente des Arrays nicht equalsrichtig implementiert werden. Ich weiß, es ist eine sehr naive Aussage, aber es gibt einen sehr wichtigen, weniger offensichtlichen Fall: Betrachten Sie ein 2D-Array.

2D-Array in Java ist ein Array von Arrays und Arrays ' equals sind defekt (oder nutzlos, wenn Sie dies bevorzugen), sodass Arrays.equals(array1, array2)sie bei 2D-Arrays nicht wie erwartet funktionieren.

Hoffentlich hilft das.

alf
quelle
13
Es ist nicht kaputt, es ist nur von Object geerbt.
Michael Borgwardt
Hat ein Array eine benutzerdefinierte Implementierung für equals()? Ich dachte, es wurde nicht von Object überschrieben.
Martijn Courteaux
@MichaelBorgwardt Es ist eine Systembibliothek mit einer Methode, die nicht das tut, was im Javadoc gesagt wird. Klingt für mich kaputt genug. Trotzdem gebe ich zu, dass es eine sehr umstrittene Aussage ist, aber ich glaube, dass "es ist kaputt" besser in Erinnerung bleibt und es daher viel bequemer ist, es so zu sehen.
alf
@ MartinijnCourteaux das ist genau das Problem :)
alf
3
Für Arrays von Arrays benötigen Sie Arrays.deepEquals--- es ist das, was someArray.equalsdie ganze Zeit hätte tun sollen. (Siehe auch : Objects.deepEquals.)
Kevin J. Chase
16

Schauen Sie sich die Implementierung der beiden Methoden an, um sie genau zu verstehen:

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

während:

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

Seufzer. In den 70er Jahren war ich der "Systemprogrammierer" (sysadmin) für ein IBM 370-System, und mein Arbeitgeber war Mitglied der IBM-Benutzergruppe SHARE. Es kam manchmal vor, dass jemand einen APAR (Fehlerbericht) über ein unerwartetes Verhalten eines CMS-Befehls übermittelte und IBM auf NOTABUG antwortete: Der Befehl macht das, wofür er entwickelt wurde (und was in der Dokumentation steht).

SHARE hat sich einen Zähler dafür ausgedacht: BAD - Broken As Designed. Ich denke, dies könnte für diese Implementierung von Equals for Arrays gelten.

An der Implementierung von Object.equals ist nichts auszusetzen. Das Objekt hat keine Datenelemente, daher gibt es nichts zu vergleichen. Zwei "Objekte" sind genau dann gleich, wenn sie tatsächlich dasselbe Objekt sind (intern dieselbe Adresse und Länge).

Diese Logik gilt jedoch nicht für Arrays. Arrays haben Daten, und Sie erwarten einen Vergleich (über Gleichheit), um die Daten zu vergleichen. Idealerweise so wie Arrays.deepEquals, aber zumindest so wie Arrays.equals (flacher Vergleich der Elemente).

Das Problem ist also, dass das Array (als integriertes Objekt) Object.equals nicht überschreibt. String (als benannte Klasse) tut überschreiben Object.equals und geben Sie das Ergebnis Sie erwarten.

Andere Antworten sind richtig: [...]. Equals ([....]) vergleicht einfach die Zeiger und nicht den Inhalt. Vielleicht wird irgendwann jemand das korrigieren. Oder vielleicht auch nicht: Wie viele bestehende Programme würden kaputt gehen, wenn [...] tatsächlich die Elemente verglichen würden? Ich vermute, nicht viele, aber mehr als null.

AP Damien
quelle
5
Ich mag das Akronym Broken.As.Designed
Chris
5

Arrays erben equals()von Objectund vergleichen daher nur true, wenn ein Array mit sich selbst verglichen wird.

Arrays.equalsVergleicht andererseits die Elemente der Arrays.

Dieses Snippet verdeutlicht den Unterschied:

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

Siehe auch Arrays.equals(). Eine andere statische Methode kann ebenfalls von Interesse sein : Arrays.deepEquals().

Adam Zalcman
quelle
1

Die Arrays.equals(array1, array2):

Überprüfen Sie, ob beide Arrays die gleiche Anzahl von Elementen enthalten und alle entsprechenden Elementpaare in den beiden Arrays gleich sind.

Die array1.equals(array2):

Vergleichen Sie das Objekt mit einem anderen Objekt und geben Sie nur dann true zurück, wenn die Referenz der beiden Objekte gleich ist wie in der Object.equals()

Aleroot
quelle
0

Das equals()von Arrays wird von geerbt Object, sodass es nicht den Inhalt der Arrays betrachtet, sondern nur jedes Array als gleich betrachtet.

Die Arrays.equals()Methoden haben die Arrays' Inhalte vergleichen. Es gibt Überladungen für alle primitiven Typen, und die für Objekte verwendet die eigenen equals()Methoden der Objekte .

Michael Borgwardt
quelle
2
Sie sagen "Inhalt von Arrays". Bedeutet dies auch mehrdimensionale Arrays?
AlanFoster
@ AlanFoster: nein. Mehrdimensionale Arrays sind Arrays von Arrays, was bedeutet, dass die Methode Arrays.equals (Object [], Object []) aufgerufen wird, die die equals () -Methoden der Sub-Arrays aufruft
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));

   }
}

Hier ist die Ausgabe:

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

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

Wenn ich diese Art von Problem sehe, würde ich mich persönlich für Arrays.equals(array1, array2)Ihre Frage entscheiden, um Verwirrung zu vermeiden.

Tayyab
quelle
Es scheint richtig zu sein, aber bei Arrays ist auch die Reihenfolge der Elemente wichtig. Wenn Sie beispielsweise ein anderes Array haben Object [] array4 = new Object [] {123, 1}; Mit Arrays.equals (array3, array4) wird false zurückgegeben.
Jiahao