Jak metoda ArrayList zawiera () ocenia obiekty?

303

Załóżmy, że utworzyłem jeden obiekt i dodałem go do mojego ArrayList. Jeśli następnie utworzę inny obiekt z dokładnie tym samym wejściem konstruktora, czy contains()metoda oceni te dwa obiekty jako takie same? Załóżmy, że konstruktor nie robi nic śmiesznego z danymi wejściowymi, a zmienne przechowywane w obu obiektach są identyczne.

ArrayList<Thing> basket = new ArrayList<Thing>();  
Thing thing = new Thing(100);  
basket.add(thing);  
Thing another = new Thing(100);  
basket.contains(another); // true or false?

class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    equals (Thing x) {
        if (x.value == value) return true;
        return false;
    }
}

Czy w ten sposób classnależy wprowadzić contains()zwrot, aby uzyskać zwrot true?

Mantas Vidutis
źródło

Odpowiedzi:

339

ArrayList implementsthe List Interface.

Jeśli spojrzeć na Javadoc dlaList w containssposób widać, że używa się equals()metody, aby ocenić, czy dwa obiekty są takie same.

Binary Nerd
źródło
61
Na wypadek, gdybyś planował zastąpić equals (), upewnij się, że przesłoniłeś również metodę hashcode (). Jeśli nie, rzeczy mogą nie działać zgodnie z oczekiwaniami podczas korzystania z kolekcji?
Mohd Farid,
34
To poprawna odpowiedź, ale pamiętaj, że musisz zmienić metodę równości, aby zaakceptować Objectraczej niż Thing. Jeśli tego nie zrobisz, metoda równości nie zostanie użyta. :)
mdierker
1
Właśnie odkryłem dla siebie, że zaćmienie ma „Generuj hashCode () i równa się” w menu Źródło.
Volodymyr Krupach
Odpowiada to na pytanie w tytule, ale nie na pytanie w opisie, tj. „Jeśli następnie utworzę inny obiekt z dokładnie tym samym wejściem konstruktora, czy metoda zawiera () oceni te dwa obiekty jako takie same?”
robguinness
3
Collectionsrobić swoje rzeczy w sposób zoptymalizowany, co oznacza, że contains()najpierw sprawdza hashCodes dwóch obiektów, a dopiero potem wywołuje equals(). Jeśli hashCodes są różne (co zawsze ma miejsce w dwóch różnych przypadkach Thing), equals()metoda nie zostanie wywołana. Zasadniczo, kiedy przesłonisz equals(), nie powinieneś również zapomnieć o przesłonięciu hashCode().
Sevastyan Savanyuk
52

Myślę, że powinny to być właściwe wdrożenia

public class Thing
{
    public int value;  

    public Thing (int x)
    {
        this.value = x;
    }

    @Override
    public boolean equals(Object object)
    {
        boolean sameSame = false;

        if (object != null && object instanceof Thing)
        {
            sameSame = this.value == ((Thing) object).value;
        }

        return sameSame;
    }
}
ChristopheCVB
źródło
1
ifinstrukcja jest niepotrzebna. instanceofwystarczy.
Paul
@Paul, o której części wypowiedzi mówisz?
ChristopheCVB
4
object != nullWarunek nie jest konieczny, ponieważ object instanceof Thingsprawdza obiekt nie jest zbyt NULL.
Alexander Farber
15

ArrayList używa metody equals zaimplementowanej w klasie (klasa Thing w twoim przypadku) do wykonania porównania equals.

Bhushan Bhangale
źródło
12

Zasadniczo powinieneś również nadpisywać za hashCode()każdym razem equals(), gdy nadpisujesz , nawet jeśli tylko dla zwiększenia wydajności. HashCode()decyduje, w którym „segmencie” Twój obiekt zostanie posortowany podczas porównywania, więc wszelkie dwa obiekty, które equal()zostaną ocenione jako prawda, powinny zwrócić to samo hashCode value(). Nie pamiętam domyślnego zachowania hashCode()(jeśli zwraca 0, wówczas kod powinien działać, ale powoli, ale jeśli zwróci adres, kod nie powiedzie się). Pamiętam kilka razy, kiedy mój kod zawiódł, ponieważ zapomniałem nadpisać hashCode(). :)

alexloh
źródło
7

Używa metody równości na obiektach. Tak więc, chyba że Przesłonięcie Thing jest równe i używa zmiennych przechowywanych w obiektach do porównania, nie zwróci true dla contains()metody.

Yishai
źródło
6
class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    equals (Thing x) {
        if (x.value == value) return true;
        return false;
    }
}

Musisz napisać:

class Thing {  
    public int value;  

    public Thing (int x) {
        value = x;
    }

    public boolean equals (Object o) {
    Thing x = (Thing) o;
        if (x.value == value) return true;
        return false;
    }
}

Teraz działa ;)

Davide
źródło
6
nie powinieneś robić rzeczy x = (rzecz) o; bez uprzedniego sprawdzenia, czy drugi obiekt ma wartość zerową
steelshark,
5

Chciałem tylko zauważyć, że następująca implementacja jest niepoprawna, gdy valuenie jest typem pierwotnym:

public class Thing
{
    public Object value;  

    public Thing (Object x)
    {
        this.value = x;
    }

    @Override
    public boolean equals(Object object)
    {
        boolean sameSame = false;

        if (object != null && object instanceof Thing)
        {
            sameSame = this.value == ((Thing) object).value;
        }

        return sameSame;
    }
}

W takim przypadku proponuję:

public class Thing {
    public Object value;  

    public Thing (Object x) {
        value = x;
    }

    @Override
    public boolean equals(Object object) {

        if (object != null && object instanceof Thing) {
            Thing thing = (Thing) object;
            if (value == null) {
                return (thing.value == null);
            }
            else {
                return value.equals(thing.value);
            }
        }

        return false;
    }
}
Caner
źródło
jak to zaimplementować, eliminując duplikat?
Sujay,
4

Inne plakaty dotyczyły pytania o sposób działania funkcji zawiera ().

Równie ważnym aspektem twojego pytania jest to, jak poprawnie wdrożyć equals (). Odpowiedź na to pytanie naprawdę zależy od tego, co stanowi równość obiektów dla tej konkretnej klasy. W podanym przykładzie, jeśli masz dwa różne obiekty, które mają x = 5, czy są one równe? To naprawdę zależy od tego, co próbujesz zrobić.

Jeśli interesuje Cię tylko równość obiektów, wówczas domyślna implementacja .equals () (ta dostarczona przez Object) używa tylko tożsamości (tj. This == other). Jeśli tego właśnie chcesz, to po prostu nie implementuj równości () w swojej klasie (niech dziedziczy po Object). Kod, który napisałeś, choć poprawny, jeśli szukasz tożsamości, nigdy nie pojawiłby się w prawdziwej klasie b / c, ale nie zapewnia żadnej korzyści w porównaniu z użyciem domyślnej implementacji Object.equals ().

Jeśli dopiero zaczynasz pracę z tymi rzeczami, zdecydowanie polecam książkę Effective Java autorstwa Joshua Blocha. Jest to świetna lektura i obejmuje tego rodzaju rzeczy (plus jak poprawnie zaimplementować equals (), gdy próbujesz zrobić więcej niż porównania oparte na tożsamości)

Dzień Kevina
źródło
Dla moich celów próbowałem sprawdzić, czy obiekt o równej wartości znajduje się w ArrayList. Przypuszczam, że to rodzaj włamania. Dziękuję za rekomendację książki
Mantas Vidutis
3

Skrót z JavaDoc :

boolean zawiera (Obiekt o)

Zwraca wartość true, jeśli ta lista zawiera określony element. Bardziej formalnie, zwraca true, jeśli tylko ta lista zawiera co najmniej jeden element e taki, że (o == null? E == null: o.equals (e))

DenisKolodin
źródło