Comment la méthode contains () d'un ArrayList évalue-t-elle les objets?

303

Supposons que je crée un objet et que je l'ajoute à mon ArrayList. Si je crée ensuite un autre objet avec exactement la même entrée de constructeur, la contains()méthode évaluera-t-elle que les deux objets sont identiques? Supposons que le constructeur ne fasse rien de drôle avec l'entrée et que les variables stockées dans les deux objets sont identiques.

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

Est-ce ainsi que le classdevrait être mis en œuvre pour avoir un contains()retour true?

Mantas Vidutis
la source

Réponses:

339

ArrayList implementsl'interface de liste.

Si vous regardez le Javadoc pourList la containsméthode, vous verrez qu'il utilise la equals()méthode pour évaluer si deux objets sont identiques.

Nerd binaire
la source
61
Juste au cas où vous envisagez de remplacer equals (), assurez-vous de remplacer également la méthode hashcode (). Si vous ne le faites pas, les choses peuvent ne pas fonctionner comme prévu lors de l'utilisation des collections?
Mohd Farid
34
C'est une bonne réponse, mais notez que vous devez changer votre méthode d'égalité pour accepter un Objectplutôt qu'un Thing. Sinon, votre méthode d'égalité ne sera pas utilisée. :)
mdierker
1
Je viens de découvrir par moi-même qu'éclipse a "Générer hashCode () et égal" dans le menu Source.
Volodymyr Krupach
Cela répond à la question dans le titre, mais pas à la question dans la description, c'est-à-dire "Si je crée ensuite un autre objet avec exactement la même entrée de constructeur, la méthode contains () évaluera-t-elle que les deux objets sont identiques?"
robguinness
3
Collectionsfaire leur travail de manière optimisée, ce qui signifie que contains()vérifie d'abord le hashCodes des deux objets, puis seulement appelle equals(). Si les hashCodes sont différents (ce qui est toujours le cas pour deux instances différentes de Thing), la equals()méthode ne sera pas appelée. En règle générale, lorsque vous remplacez equals(), vous ne devez pas oublier de remplacer hashCode()également.
Sevastyan Savanyuk
52

Je pense que les bonnes implémentations devraient être

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
la source
1
ifn'est pas nécessaire. instanceofest assez.
Paul
@Paul de quelle partie de la déclaration vous parlez?
ChristopheCVB
4
La object != nullcondition n'est pas nécessaire, car elle object instanceof Thingvérifie que l'objet n'est pas également nul.
Alexander Farber
15

L'ArrayList utilise la méthode equals implémentée dans la classe (votre classe Thing case) pour faire la comparaison des égaux.

Bhushan Bhangale
la source
12

En règle générale, vous devez également remplacer hashCode()chaque fois que vous remplacez equals(), même si ce n'est que pour l'amélioration des performances. HashCode()décide dans quel «compartiment» votre objet est trié lors de la comparaison, de sorte que deux objets equal()évalués à true doivent renvoyer le même hashCode value(). Je ne me souviens pas du comportement par défaut de hashCode()(s'il renvoie 0, alors votre code devrait fonctionner mais lentement, mais s'il renvoie l'adresse, votre code échouera). Je me souviens d'un tas de fois où mon code a échoué car j'ai oublié de remplacer hashCode(). :)

alexloh
la source
7

Il utilise la méthode equals sur les objets. Donc, à moins que Thing ne remplace égal et utilise les variables stockées dans les objets à des fins de comparaison, il ne renverra pas true sur la contains()méthode.

Yishai
la source
6
class Thing {  
    public int value;  

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

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

Vous devez écrire:

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

Maintenant ça marche ;)

Davide
la source
6
vous ne devriez pas faire Chose x = (Chose) o; sans d'abord vérifier si l'autre objet est nul
steelshark
5

Je voulais juste noter que l'implémentation suivante est incorrecte quand ce valuen'est pas un type primitif:

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

Dans ce cas, je propose ce qui suit:

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
la source
comment mettre en œuvre cela tout en éliminant le doublon?
Sujay
4

D'autres affiches ont abordé la question de savoir comment contient () fonctionne.

Un aspect tout aussi important de votre question est de savoir comment implémenter correctement equals (). Et la réponse à cela dépend vraiment de ce qui constitue l'égalité d'objet pour cette classe particulière. Dans l'exemple que vous avez fourni, si vous avez deux objets différents qui ont tous les deux x = 5, sont-ils égaux? Cela dépend vraiment de ce que vous essayez de faire.

Si vous n'êtes intéressé que par l'égalité des objets, l' implémentation par défaut de .equals () (celle fournie par Object) utilise uniquement l'identité (c'est-à-dire ceci == autre). Si c'est ce que vous voulez, alors n'implémentez pas equals () sur votre classe (laissez-la hériter d'Object). Le code que vous avez écrit, bien qu'il soit correct si vous optez pour l'identité, n'apparaîtrait jamais dans une vraie classe b / c, il n'offre aucun avantage par rapport à l'utilisation de l'implémentation Object.equals () par défaut.

Si vous débutez avec ce genre de choses, je recommande fortement le livre Effective Java de Joshua Bloch. C'est une excellente lecture et couvre ce genre de chose (plus comment implémenter correctement equals () lorsque vous essayez de faire plus que des comparaisons basées sur l'identité)

Kevin Day
la source
Dans mon but, j'essayais de voir si un objet de valeur égale se trouvait dans la liste de tableaux. Je suppose que c'est une sorte de hack. Merci pour la recommandation du livre
Mantas Vidutis
3

Raccourci depuis JavaDoc :

booléen contient (Object o)

Renvoie true si cette liste contient l'élément spécifié. Plus formellement, retourne vrai si et seulement si cette liste contient au moins un élément e tel que (o == null? E == null: o.equals (e))

DenisKolodin
la source