Como o método contains () de um ArrayList avalia objetos?

303

Digamos que eu crie um objeto e o adicione ao meu ArrayList. Se eu criar outro objeto com exatamente a mesma entrada do construtor, o contains()método avaliará os dois objetos como sendo os mesmos? Suponha que o construtor não faça nada de engraçado com a entrada e as variáveis ​​armazenadas nos dois objetos são 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;
    }
}

É assim que o classdeve ser implementado para ter contains()retorno true?

Mantas Vidutis
fonte

Respostas:

339

MatrizLista implementsa interface da lista.

Se você olhar para o JavadocList no containsmétodo, verá que ele usa o equals()método para avaliar se dois objetos são iguais.

Nerd binário
fonte
61
Caso você planeje substituir equals (), certifique-se de substituir também o método hashcode (). Se não, as coisas podem não funcionar como esperado ao usar Coleções?
Mohd Farid
34
Esta é uma resposta correta, mas observe que você precisa alterar seu método equals para aceitar um Objecte não um Thing. Caso contrário, seu método equals não será usado. :)
mdierker
1
Acabei de descobrir por mim mesmo que o eclipse tem "Generate hashCode () and equal") no menu Source.
Volodymyr Krupach
Isso responde à pergunta no título, mas não à pergunta na descrição, ou seja, "Se eu criar outro objeto com exatamente a mesma entrada do construtor, o método contains () avaliará os dois objetos como iguais?"
robguinness
3
Collectionsfaça suas coisas de maneira otimizada, o que significa que contains()primeiro verifica os hashCodes dos dois objetos e só então chama equals(). Se os hashCodeforem diferentes (o que sempre ocorre em duas instâncias diferentes de Thing), o equals()método não será chamado. Como regra geral, quando você substitui equals(), não se esqueça de substituí-lo hashCode()também.
Sevastyan Savanyuk
52

Eu acho que as implementações certas devem 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
fonte
1
ifdeclaração é desnecessária. instanceofbasta.
Paul
@Paul, de que parte da declaração você está falando?
22416 ChristopheCVB
4
A object != nullcondição é desnecessária, porque também object instanceof Thingverifica se o objeto não é nulo.
Alexander Farber
15

O ArrayList usa o método equals implementado na classe (a classe Thing do seu caso) para fazer a comparação igual.

Bhushan Bhangale
fonte
12

Geralmente, você também deve substituir hashCode()sempre que substituir equals(), mesmo que seja apenas pelo aumento de desempenho. HashCode()decide em qual 'bloco' o seu objeto é classificado ao fazer uma comparação; portanto, quaisquer dois objetos equal()avaliados como true devem retornar o mesmo hashCode value(). Não me lembro do comportamento padrão de hashCode()(se retornar 0, seu código deverá funcionar lentamente, mas se retornar o endereço, seu código falhará). Lembro-me de várias vezes em que meu código falhou porque esqueci de substituir hashCode(). :)

alexloh
fonte
7

Ele usa o método equals nos objetos. Portanto, a menos que Thing substitua igual e use as variáveis ​​armazenadas nos objetos para comparação, ele não retornará true no contains()método.

Yishai
fonte
6
class Thing {  
    public int value;  

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

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

Você deve escrever:

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

Agora funciona;)

Davide
fonte
6
você não deve fazer Coisa x = (Coisa) o; sem primeiro verificar se o outro objeto é nulo
steelshark
5

Só queria observar que a seguinte implementação está errada quando valuenão é um 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;
    }
}

Nesse caso, proponho o seguinte:

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
fonte
como implementar isso e eliminar a duplicata?
Sujay
4

Outros pôsteres abordaram a questão sobre como o contains () funciona.

Um aspecto igualmente importante da sua pergunta é como implementar corretamente equals (). E a resposta para isso é realmente dependente do que constitui a igualdade de objetos para essa classe específica. No exemplo que você forneceu, se você tiver dois objetos diferentes, ambos com x = 5, eles são iguais? Realmente depende do que você está tentando fazer.

Se você está interessado apenas na igualdade de objetos, a implementação padrão de .equals () (a fornecida por Object) usa apenas identidade (ou seja, este == outro). Se é isso que você deseja, simplesmente não implemente equals () em sua classe (deixe herdar de Object). O código que você escreveu, embora meio que correto, se você estiver procurando por identidade, nunca apareceria em uma classe real b / c, pois não oferece nenhum benefício sobre o uso da implementação padrão Object.equals ().

Se você está apenas começando com esse material, recomendo fortemente o livro Java Efetivo de Joshua Bloch. É uma ótima leitura e abrange esse tipo de coisa (além de como implementar corretamente equals () quando você está tentando fazer mais do que comparações baseadas em identidade)

Kevin Day
fonte
Para o meu propósito, eu estava tentando ver se um objeto de igual valor estava no ArrayList. Suponho que é uma espécie de hack. Obrigado pela recomendação do livro
Mantas Vidutis
3

Atalho do JavaDoc :

booleano contém (Objeto o)

Retorna true se esta lista contiver o elemento especificado. Mais formalmente, retorna true se e somente se esta lista contiver pelo menos um elemento e tal que (o == null? E == null: o.equals (e))

DenisKolodin
fonte