jest równy vs tablice. równe w Javie


209

Czy porównując 2 tablice w Javie, występują jakieś różnice?

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

A jeśli tak, to czym one są?


Spójrz także na java.util.Arrays.deepEquals (Object [] a1, Object [] a2)
ultraon

Odpowiedzi:


299

array1.equals(array2)jest taki sam jak array1 == array2, tj. czy jest to ta sama tablica. Jak zauważa @alf, większość ludzi tego nie oczekuje.

Arrays.equals(array1, array2) porównuje zawartość tablic.


Podobnie array.toString()może nie być bardzo przydatne i musisz go użyć Arrays.toString(array).


59
Zauważ, że Arrays.equals()nie działa zgodnie z oczekiwaniami dla tablic wielowymiarowych, porównuje jedynie elementy z pierwszego wymiaru dla równości odniesienia. Apache commons ArrayUtils.isEqualswspółpracuje z tablicami wielowymiarowymi.
Adam Parkin,

4
Jestem oszołomiony. Czy istnieje powód, aby implementować array.equals do porównywania wskaźników zamiast porównywania długości i każdego obiektu?
Jezioro

2
@Lake is porównuje długość tablicy i zawartych w niej obiektów, ale nie robi to głębokiego porównania. Fakt równy działa zgodnie z oczekiwaniami, ponieważ tablice są zepsute, to nie powinno być problemem.
Peter Lawrey

48
@AdamParkin Właśnie dlatego Arrays.deepEquals(Object[], Object[]).
Elliott Frisch

3
@JeewanthaSamaraweera, która jest definicją tej metody, jednak .equalsnie porównuje zawartości, dlatego właśnie potrzebujesz tej metody.
Peter Lawrey,

86

Jest to niesławny problem: .equals()ponieważ tablice są mocno zepsute, po prostu nigdy ich nie używaj.

To powiedziawszy, nie jest „zepsute”, jak w „ktoś zrobił to naprawdę źle” - po prostu robi to, co zdefiniowano, a nie to, czego zwykle się oczekuje. Tak więc dla purystów: jest całkowicie w porządku, a to oznacza również, że nigdy go nie używaj.

Teraz oczekiwanym zachowaniem equalsjest porównywanie danych. Domyślnym zachowaniem jest porównywanie tożsamości, ponieważ Objectnie ma żadnych danych (dla purystów: tak, ale nie o to chodzi); założeniem jest, że jeśli potrzebujesz equalsw podklasach, zaimplementujesz to. W tablicach nie ma dla ciebie żadnej implementacji, więc nie powinieneś jej używać.

Różnica polega na tym, że Arrays.equals(array1, array2)działa tak, jak można się spodziewać (tzn. Porównuje zawartość), array1.equals(array2)wraca do Object.equalsimplementacji, która z kolei porównuje tożsamość, a zatem lepiej zastępuje ją ==(dla purystów: tak, wiem o tym null).

Problem w tym, że nawet Arrays.equals(array1, array2)cię ugryzie, jeśli elementy tablicy nie zostaną equalspoprawnie zaimplementowane . To bardzo naiwne stwierdzenie, wiem, ale istnieje bardzo ważny, mniej oczywisty przypadek: rozważ tablicę 2D.

Tablica 2D w Javie to tablica tablic, a tablice equalssą zepsute (lub bezużyteczne, jeśli wolisz), więc Arrays.equals(array1, array2)nie będą działać tak, jak oczekujesz na tablicach 2D.

Mam nadzieję, że to pomaga.


13
Nie jest zepsuty, tylko odziedziczony po Object.
Michael Borgwardt

Czy tablica ma niestandardową implementację equals()? Myślałem, że nie zostało zastąpione przez Object.
Martijn Courteaux

@MichaelBorgwardt to biblioteka systemowa z metodą, która nie robi tego, co zostało powiedziane w javadoc. Brzmi dla mnie wystarczająco mocno. To powiedziawszy, przyznaję, że jest to bardzo dyskusyjna wypowiedź, ale wierzę, że „jest zepsute” lepiej zapamiętane, a zatem o wiele wygodniej jest myśleć o tym w ten sposób.
alf

@MartijnCourteaux to jest właśnie problem :)
alf.

3
W przypadku tablic tablic potrzebujesz Arrays.deepEquals--- to właśnie someArray.equalspowinno być zrobione przez cały czas. (Powiązane Objects.deepEquals
Kevin J. Chase

16

Zajrzyj do implementacji dwóch metod, aby je głęboko zrozumieć:

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

podczas:

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

11

Westchnienie. W latach 70. byłem „programistą systemu” (sysadmin) dla systemu IBM 370, a mój pracodawca był członkiem grupy użytkowników IBM SHARE. Czasami zdarza się, że ktoś przedłożył raport APAR (raport o błędzie) dotyczący niektórych nieoczekiwanych zachowań niektórych poleceń CMS, a IBM zareaguje NOTABUG: polecenie wykonuje to, do czego zostało przeznaczone (i co mówi dokumentacja).

SHARE wymyślił coś przeciwnego: BAD - Broken As Designed. Myślę, że może to dotyczyć implementacji równości dla tablic.

Nie ma nic złego w implementacji Object.equals. Obiekt nie ma elementów danych, więc nie ma nic do porównania. Dwa „obiekty” są równe wtedy i tylko wtedy, gdy są w rzeczywistości tym samym obiektem (wewnętrznie ten sam adres i długość).

Ale ta logika nie dotyczy tablic. Tablice zawierają dane i oczekujesz porównania (poprzez równe), aby porównać dane. Najlepiej, jak robi to Arrays.deepEquals, ale przynajmniej tak, jak robi to Arrays.equals (płytkie porównanie elementów).

Problemem jest to, że tablica (jako obiekt wbudowany) nie zastępuje Object.equals. String (w nazwie klasy) robi override Object.equals i daje wynik można oczekiwać.

Inne podane odpowiedzi są poprawne: [...]. Równa się ([....]) po prostu porównuje wskaźniki, a nie zawartość. Może kiedyś ktoś to naprawi. A może nie: ile istniejących programów zepsułoby się, gdyby [...] równa się faktycznie porównać elementy? Podejrzewam, że niewiele, ale więcej niż zero.


5
Podoba mi się skrót Broken.As.Designed
Chris

5

Tablice dziedziczą equals()od Objecta więc porównać tylko zwraca true jeśli porównać tablicę przeciwko sobie.

Z drugiej strony Arrays.equalsporównuje elementy tablic.

Ten fragment wyjaśnia różnicę:

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

Zobacz także Arrays.equals(). Inna metoda statyczna może być także interesujące: Arrays.deepEquals().


1

Arrays.equals(array1, array2):

sprawdź, czy obie tablice zawierają tę samą liczbę elementów i czy wszystkie odpowiednie pary elementów w dwóch tablicach są równe.

array1.equals(array2):

porównaj obiekt z innym obiektem i zwróć wartość true tylko wtedy, gdy odwołanie do dwóch obiektów jest równe jak w Object.equals()


0

equals()Tablic jest odziedziczony Object, więc nie patrzeć na treść arrrays, to tylko uważa każda tablica równe sobie.

Te Arrays.equals()metody nie porównać zawartość tablic. Istnieją przeciążenia dla wszystkich typów pierwotnych, a ten dla obiektów używa własnych equals()metod obiektów .


2
mówisz „zawartość tablic”, czy to oznacza także tablice wielowymiarowe?
AlanFoster

@AlanFoster: nie. Tablice wielowymiarowe są tablicami tablic, co oznacza, że ​​będą wywoływane metody Arrays.equals (Object [], Object []), które wywołują metody equals () pod-tablic
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));

   }
}

Oto wynik:

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

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

Widząc tego rodzaju problem, osobiście wybrałbym Arrays.equals(array1, array2)według twojego pytania, aby uniknąć zamieszania.


Wydaje się, że jest poprawny, ale w przypadku tablic ważna jest również kolejność elementów. Na przykład, jeśli istnieje inna tablica Object [] array4 = new Object [] {123, 1}; z Arrays.equals (array3, array4), zwróci false.
jiahao,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.