¿Cómo evalúa un objeto el método contiene () de ArrayList?

303

Digamos que creo un objeto y lo agrego a mi ArrayList. Si luego creo otro objeto con exactamente la misma entrada del constructor, ¿evaluará el contains()método que los dos objetos sean iguales? Suponga que el constructor no hace nada divertido con la entrada, y las variables almacenadas en ambos objetos son idénticas.

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

¿Es así como classdebería implementarse para tener contains()retorno true?

Mantas Vidutis
fuente

Respuestas:

339

Array implementsLista la interfaz de la lista.

Si observa el Javadoc para verList el containsmétodo, verá que utiliza el equals()método para evaluar si dos objetos son iguales.

Nerd Binario
fuente
61
En caso de que planee anular equals (), asegúrese de anular también el método hashcode (). Si no lo haces, ¿las cosas pueden no funcionar como se espera mientras usas Colecciones?
Mohd Farid
34
Esta es una respuesta correcta, pero tenga en cuenta que necesita cambiar su método de igualdad para aceptar un en Objectlugar de un Thing. Si no lo hace, su método igual no será utilizado. :)
mdierker
1
Acabo de descubrir por mí mismo que eclipse tiene "Generar hashCode () e igual" en el menú Fuente.
Volodymyr Krupach
Esto responde la pregunta en el título, pero no la pregunta en la descripción, es decir, "Si luego creo otro objeto con exactamente la misma entrada del constructor, ¿evaluará el método contenedor () que los dos objetos sean iguales?"
robguinness
3
Collectionshacer sus cosas de manera optimizada, lo que significa que contains()primero verifica los correos electrónicos hashCodede los dos objetos, y solo luego llama equals(). Si los hashCodes son diferentes (que siempre es el caso para dos instancias diferentes de Thing), equals()no se llamará al método. Como regla general, cuando anula equals(), no debe olvidar anular hashCode()también.
Sevastyan Savanyuk
52

Creo que las implementaciones correctas deberían ser

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
fuente
1
ifdeclaración es innecesaria. instanceofes suficiente.
Paul
@Paul, ¿de qué parte de la declaración estás hablando?
ChristopheCVB
44
La object != nullcondición es innecesaria, porque object instanceof Thingverifica que el objeto no sea nulo también.
Alexander Farber
15

ArrayList utiliza el método de igualdad implementado en la clase (la clase Thing de su caso) para hacer la comparación de igualdad.

Bhushan Bhangale
fuente
12

En general, también debe anular hashCode() cada vez que anula equals(), incluso si solo es para aumentar el rendimiento. HashCode()decide en qué 'cubo' se ordena su objeto al hacer una comparación, por lo que dos objetos que se equal()evalúen como verdaderos deberían devolver lo mismo hashCode value(). No puedo recordar el comportamiento predeterminado de hashCode()(si devuelve 0, entonces su código debería funcionar pero lentamente, pero si devuelve la dirección, entonces su código fallará). Sin hashCode()embargo , recuerdo muchas veces cuando mi código falló porque olvidé anularlo . :)

alexloh
fuente
7

Utiliza el método igual en los objetos. Entonces, a menos que Cosa anule la igualdad y use las variables almacenadas en los objetos para comparación, no devolverá verdadero en el contains()método.

Yishai
fuente
6
class Thing {  
    public int value;  

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

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

Debes escribir:

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

Ahora funciona ;)

Davide
fuente
66
no debes hacer Cosa x = (Cosa) o; sin comprobar primero si el otro objeto es nulo
steelshark
5

Solo quería señalar que la siguiente implementación es incorrecta cuando valueno es 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;
    }
}

En ese caso, propongo lo siguiente:

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
fuente
¿Cómo implementar esto mientras se elimina el duplicado?
Sujay
4

Otros carteles han abordado la pregunta sobre cómo funciona contiene ().

Un aspecto igualmente importante de su pregunta es cómo implementar correctamente equals (). Y la respuesta a esto depende realmente de lo que constituye la igualdad de objetos para esta clase en particular. En el ejemplo que proporcionó, si tiene dos objetos diferentes que tienen x = 5, ¿son iguales? Realmente depende de lo que intentes hacer.

Si solo está interesado en la igualdad de objetos, entonces el valor predeterminado implementación de .equals () (la provista por Object) usa solo identidad (es decir, esto == otro). Si eso es lo que desea, simplemente no implemente equals () en su clase (deje que herede de Object). El código que escribió, si bien es correcto si busca identidad, nunca aparecerá en una clase real b / c, no proporciona ningún beneficio sobre el uso de la implementación predeterminada Object.equals ().

Si recién está comenzando con estas cosas, le recomiendo encarecidamente el libro Effective Java de Joshua Bloch. Es una gran lectura, y cubre este tipo de cosas (además de cómo implementar correctamente equals () cuando intentas hacer más que comparaciones basadas en identidad)

Kevin Day
fuente
Para mi propósito, estaba tratando de ver si un objeto de igual valor estaba en ArrayList. Supongo que es una especie de truco. Gracias por la recomendación del libro
Mantas Vidutis
3

Atajo de JavaDoc :

boolean contiene (objeto o)

Devuelve verdadero si esta lista contiene el elemento especificado. Más formalmente, devuelve verdadero si y solo si esta lista contiene al menos un elemento e tal que (o == null? E == null: o.equals (e))

DenisKolodin
fuente