web-dev-qa-db-fra.com

Hashcode et Equals pour Hashset

Veuillez clarifier mon doute dans Hashset. Considérez le code suivant,

class Person
{
    String name;

    Person(String n)
    {
        name=n; 
    }
    public String getName()
    {
        return name;   
    }

    @Override
    public boolean equals(Object arg0) {

        System.out.println("in equals");

        Person obj=(Person)arg0;

        System.out.println("1st "+getName());
        System.out.println("2nd "+obj.getName());

        if(this.getName().equals(obj.getName()))
        {
                return true;
        }
        return false;
    }


    @Override
    public int hashCode() {

        System.out.println("in hash code");
        System.out.println(" value is "+Integer.valueOf(name.charAt(0)));
        return Integer.valueOf(name.charAt(0));
    }
}

en principal j'ai le code suivant

Person obj1=new Person("bcd");

Person obj2=new Person("cde");

Person obj3=new Person("abc");

Person obj4=new Person("abc");

Maintenant, si j'ajoute ces objets à hashset

Set<Person> sset=new HashSet<Person>();

sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj3);

Je reçois cette sortie

in hash code                                                                      
value is 98    
in hash code   
value is 97    
in hash code    
value is 99    
in hash code    
value is 97  
in equals  
1st abc     
2nd abc

Question 1: pourquoi la fonction equals () n'est appelée qu'une seule fois pour vérifier obj3 et obj4? Pourquoi ce n'est pas vérifié pour le reste des objets?

Question 2: Si la réponse est parce qu'ils ont tous les deux le même code de hachage, alors seulement les égaux seront appelés, alors pourquoi ce n'est pas appelé pour le code ci-dessous

sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj4);

la sortie est:

in hash code  
value is 98  
in hash code   
value is 97   
in hash code   
value is 99   
in hash code  
value is 97 

Il ne va pas à l'intérieur de la méthode equals () même si deux mêmes objets sont ajoutés à l'ensemble de hachage qui a le même code de hachage.

Question: J'ai itéré la valeur ci-dessus et imprimé le contenu mais ni hachage ni égal n'ont été appelés. quand il est vraiment utile de remplacer le hashcode et la méthode equals?

Question 4: Quand est-ce que hashCode() et equals() seront appelés?

46
Mojoy
  1. Il n'est pas nécessaire d'appeler equals si hashCode diffère.
  2. Il n'est pas nécessaire d'appeler hashCode si (obj1 == obj2).
  3. Il n'y a pas besoin de hashCode et/ou equals juste pour itérer - vous ne comparez pas les objets
  4. Lorsque nécessaire pour distinguer les objets.
53
Erik

Je pense que toutes vos questions recevront une réponse si vous comprenez comment fonctionnent les Sets, et en particulier les HashSets. Un ensemble est une collection d'objets uniques, avec Java définissant l'unicité en ce qu'il ne correspond à rien d'autre (est égal à false).

Le HashSet profite des codes de hachage pour accélérer les choses. Il suppose que deux objets égaux auront le même code de hachage. Cependant, cela ne suppose pas que deux objets avec le même code de hachage signifient qu'ils sont égaux. C'est pourquoi, lorsqu'il détecte un code de hachage entrant en collision, il ne compare avec d'autres objets (dans votre cas, un) dans l'ensemble avec le même code de hachage.

19
Zugwalt

selon le code source jdk de javasourcecode.org, HashSet utilise HashMap comme implémentation interne, le code sur la méthode put de HashSet est ci-dessous:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

La règle est tout d'abord de vérifier le hachage, puis de vérifier la référence et ensuite la méthode call equals de l'objet sera insérée.

11
Sion.Cheng

Parce que dans le deuxième cas, vous ajoutez deux fois la même référence et HashSet vérifiez cela dans HashMap.put() sur lequel HashSet est basé:

        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }

Comme vous pouvez le voir, equals ne sera appelé que si le hachage de la clé ajoutée est égal à la clé déjà présente dans set et les références de ces deux sont différentes.

2
Victor Sorokin

Vous devriez lire comment vous assurer que vous avez correctement implémenté equals et hashCode. C'est un bon point de départ: Quels sont les problèmes à prendre en compte lors de la substitution d'equals et de hashCode en Java?

1
Greg