In che modo il metodo contiene () di un ArrayList valuta gli oggetti?


303

Di 'che creo un oggetto e lo aggiungo al mio ArrayList. Se quindi creo un altro oggetto con esattamente lo stesso input del costruttore, il contains()metodo valuterà i due oggetti come uguali? Supponiamo che il costruttore non faccia nulla di divertente con l'input e che le variabili memorizzate in entrambi gli oggetti siano identiche.

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

È così che classdovrebbe essere implementato per avere contains()ritorno true?

Risposte:


339

Array implementsElenca l'interfaccia elenco.

Se osservi Javadoc perList il containsmetodo, vedrai che utilizza il equals()metodo per valutare se due oggetti sono uguali.


61
Nel caso in cui prevedia di sovrascrivere equals (), assicurati di sovrascrivere anche il metodo hashcode (). In caso contrario, le cose potrebbero non funzionare come previsto durante l'utilizzo delle Collezioni?
Mohd Farid,

34
Questa è una risposta corretta, ma nota che devi cambiare il tuo metodo uguale per accettare un Objectanziché un Thing. In caso contrario, il metodo equals non verrà utilizzato. :)
mdierker,

1
Ho appena scoperto che eclipse ha "Genera hashCode () ed è uguale" nel menu Sorgente.
Volodymyr Krupach,

Ciò risponde alla domanda nel titolo, ma non alla domanda nella descrizione, ad esempio "Se creo un altro oggetto con lo stesso input del costruttore, il metodo contenga () valuterà i due oggetti come uguali?"
robguinness,

3
Collectionsfanno le loro cose in modo ottimizzato, il che significa che contains()controlla prima la hashCodes dei due oggetti e solo allora chiama equals(). Se le hashCodes sono diverse (come sempre per due diverse istanze di Thing), il equals()metodo non verrà chiamato. Come regola generale, quando si esegue l'override equals(), non si deve dimenticare di ignorare hashCode()anche.
Sevastyan Savanyuk,

52

Penso che dovrebbero essere le giuste implementazioni

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

1
ifl'affermazione non è necessaria. instanceofè abbastanza.
Paul

@Paul di quale parte dell'affermazione stai parlando?
ChristopheCVB,

4
La object != nullcondizione non è necessaria, poiché object instanceof Thingverifica che l'oggetto non sia anch'esso nullo.
Alexander Farber,

15

ArrayList utilizza il metodo equals implementato nella classe (la tua classe Thing case) per fare il confronto equals.


12

In genere, è necessario eseguire hashCode()l' override ogni volta che si esegue l'override equals(), anche se solo per l'aumento delle prestazioni. HashCode()decide in quale 'secchio' il tuo oggetto viene ordinato quando si fa un confronto, quindi ogni due oggetti che equal()valuta vero dovrebbe restituire lo stesso hashCode value(). Non ricordo il comportamento predefinito di hashCode()(se restituisce 0, il codice dovrebbe funzionare ma lentamente, ma se restituisce l'indirizzo, il codice fallirà). Ricordo un sacco di volte in cui il mio codice falliva perché mi ero dimenticato di ignorare hashCode(). :)


7

Usa il metodo equals sugli oggetti. Quindi, a meno che Thing non abbia la stessa priorità e utilizzi le variabili memorizzate negli oggetti per il confronto, non restituirà true sul contains()metodo.


6
class Thing {  
    public int value;  

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

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

Devi scrivere:

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

Ora funziona ;)


6
non dovresti fare Cosa x = (Cosa) o; senza prima verificare se l'altro oggetto è null
steelshark

5

Volevo solo notare che la seguente implementazione è sbagliata quando valuenon è un tipo primitivo:

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

In tal caso, propongo quanto segue:

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

come implementarlo eliminando il duplicato?
Sujay,

4

Altri poster hanno affrontato la domanda su come contiene () funziona.

Un aspetto altrettanto importante della tua domanda è come implementare correttamente equals (). E la risposta a ciò dipende in realtà da ciò che costituisce l'uguaglianza degli oggetti per questa particolare classe. Nell'esempio che hai fornito, se hai due oggetti diversi che hanno entrambi x = 5, sono uguali? Dipende davvero da cosa stai cercando di fare.

Se sei interessato solo all'uguaglianza degli oggetti, allora l' impostazione predefinita implementazione di .equals () (quella fornita da Object) usa solo l'identità (cioè this == altro). Se è quello che vuoi, allora non implementare equals () sulla tua classe (lascia che erediti da Object). Il codice che hai scritto, sebbene sia corretto se stai andando per l'identità, non apparirebbe mai in una vera classe b / c, non offre alcun vantaggio rispetto all'uso dell'implementazione Object.equals () predefinita.

Se hai appena iniziato con questa roba, consiglio vivamente il libro Effective Java di Joshua Bloch. È un'ottima lettura e copre questo genere di cose (oltre a come implementare correttamente equals () quando si sta provando a fare qualcosa di più rispetto ai confronti basati sull'identità)


Per il mio scopo, stavo cercando di vedere se un oggetto di uguale valore fosse presente nell'ArrayList. Suppongo sia una specie di hack. Grazie per la raccomandazione sul libro
Mantas Vidutis,

3

Collegamento da JavaDoc :

booleano contiene (oggetto o)

Restituisce vero se questo elenco contiene l'elemento specificato. Più formalmente, restituisce vero se e solo se questo elenco contiene almeno un elemento e tale che (o == null? E == null: o.equals (e))

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.