Pourquoi dois-je remplacer les méthodes equals et hashCode en Java?


383

Récemment, j'ai lu ce document Developer Works .

Le document est tout au sujet de définir hashCode()et equals()efficacement et correctement, mais je ne suis pas en mesure de comprendre pourquoi nous devons remplacer ces deux méthodes.

Comment prendre la décision de mettre en œuvre ces méthodes efficacement?


4
Il y a deux excellents articles sur Programming.Guide expliquant exactement ceci: Quand devrais-je remplacer Equals? et Pourquoi vous devez toujours remplacer hashCode lorsque vous remplacez equals . (Attention, la réponse acceptée est en fait fausse.)
aioobe

Le dépassement de cas est égal à: deux mêmes objets auront un code de hachage différent = les mêmes objets seront placés dans un compartiment différent (duplication). Case Override only hashcode: deux mêmes objets auront le même hashcode = le même objet ira dans le même compartiment (duplication).
VdeX

Réponses:


524

Joshua Bloch dit sur Java efficace

Vous devez remplacer hashCode () dans chaque classe qui remplace equals (). Ne pas le faire entraînera une violation du contrat général pour Object.hashCode (), ce qui empêchera votre classe de fonctionner correctement en conjonction avec toutes les collections basées sur le hachage, y compris HashMap, HashSet et Hashtable.

Essayons de le comprendre avec un exemple de ce qui se passerait si nous remplaçons equals()sans remplacer hashCode()et essayons d'utiliser a Map.

Disons que nous avons une classe comme celle-ci et que deux objets de MyClasssont égaux si leur importantFieldest égal (avec hashCode()et equals()généré par éclipse)

public class MyClass {

    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    public String getEqualField() {
        return importantField;
    }

    public String getAnotherField() {
        return anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }

}

Remplacer uniquement equals

Si seulement equalsest remplacé, alors lorsque vous appelez d' myMap.put(first,someValue)abord, il hachera dans un compartiment et lorsque vous appellerez, myMap.put(second,someOtherValue)il hachera dans un autre compartiment (car ils ont un autre hashCode). Ainsi, bien qu'ils soient égaux, car ils ne hachent pas le même compartiment, la carte ne peut pas le réaliser et les deux restent dans la carte.


Bien qu'il ne soit pas nécessaire de remplacer equals()si nous remplaçons hashCode(), voyons ce qui se passerait dans ce cas particulier où nous savons que deux objets de MyClasssont égaux si leur importantFieldest égal mais nous ne remplaçons pas equals().

Remplacer uniquement hashCode

Imaginez que vous avez ceci

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Si vous ne remplacez que hashCodelorsque vous appelez, myMap.put(first,someValue)il prend d'abord, calcule son hashCodeet le stocke dans un compartiment donné. Ensuite, lorsque vous appelez, myMap.put(second,someOtherValue)il doit remplacer d'abord par second selon la documentation de la carte car ils sont égaux (selon les besoins de l'entreprise).

Mais le problème est que égal n'a pas été redéfini, donc lorsque la carte hache secondet itère à travers le compartiment en recherchant s'il y a un objet ktel qu'il second.equals(k)est vrai, il n'en trouvera pas comme second.equals(first)il le sera false.

J'espère que c'était clair


5
pouvez-vous s'il vous plaît élaborer un peu plus, dans le deuxième cas, pourquoi le deuxième objet doit aller dans un autre seau?
Hussain Akhtar Wahid 'Ghouri'

57
Je n'aime pas cette réponse car elle suggère que vous ne pouvez pas remplacer hashCode () sans remplacer equals (), ce qui n'est tout simplement pas vrai. Vous dites que votre exemple de code (la partie "remplacer uniquement hashCode") ne fonctionnera pas parce que vous définissez vos deux objets comme égaux, mais - désolé - cette définition n'est que dans votre tête. Dans votre premier exemple, vous avez deux objets non égaux avec le même hashCode, et c'est parfaitement légal. Donc, la raison pour laquelle vous devez remplacer equals () n'est pas parce que vous avez déjà surchargé hashCode (), mais parce que vous voulez déplacer votre définition "equals" de votre tête vers le code.
user2543253

11
if you think you need to override one, then you need to override both of themest faux. Vous devez remplacer hashCodesi votre classe remplace equalsmais inverser n'est pas vrai.
akhil_mittal du

4
Je pense qu'il est tout à fait correct de remplacer uniquement hashCode () sans remplacer également equals (). C'est aussi ce qui est écrit en Java efficace : books.google.fr/…
Johnny

2
@PhantomReference, notez que seule la substitution equalsviolerait le contrat énoncé dans le javadoc de Object: "Si deux objets sont égaux selon la equals(Object)méthode, alors appeler la hashCodeméthode sur chacun des deux objets doit produire le même résultat entier."Bien sûr, toutes les parties de tous les contrats ne sont pas exercées dans tout le code, mais, officiellement, c'est une violation et je considère que c'est un bug qui attend.
aioobe

264

Les collections telles que HashMapet HashSetutilisent une valeur de code de hachage d'un objet pour déterminer comment il doit être stocké dans une collection, et le code de hachage est à nouveau utilisé afin de localiser l'objet dans sa collection.

La récupération de hachage est un processus en deux étapes:

  1. Trouvez le bon seau (en utilisant hashCode() )
  2. Rechercher le bon élément dans le seau (en utilisant equals())

Voici un petit exemple sur la raison pour laquelle nous devrions remplacer equals()ethashcode() .

Considérons une Employeeclasse qui a deux champs: l'âge et le nom.

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

Créez maintenant une classe, insérez un Employeeobjet dans a HashSetet testez si cet objet est présent ou non.

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

Il imprimera les éléments suivants:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

Désactivez maintenant hashcode() méthode, exécutez la même et le résultat serait:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Maintenant, pouvez-vous voir pourquoi si deux objets sont considérés comme égaux, leur code de hachage doit également être égal? Sinon, vous ne seriez jamais en mesure de trouver l'objet puisque la méthode de hachage par défaut dans la classe Object propose pratiquement toujours un numéro unique pour chaque objet, même si la equals()méthode est remplacée de telle manière que deux ou plusieurs objets sont considérés comme égaux . Peu importe à quel point les objets sont égaux si leurs codes de hachage ne reflètent pas cela. Donc encore une fois: si deux objets sont égaux, leurs codes de hachage doivent également être égaux.


4
Exemple parfait. Démontre clairement la différence!
coderpc

3
nice expliqué @rajeev
VdeX

2
@VikasVerma est égal à l'objet aura un code de hachage égal ne signifie pas qu'un objet différent aura un code de hachage différent. Que se passe-t-il si les objets sont réellement différents, mais que leur code de hachage est le même?
Ravi

1
expliqué très bien :)
Rahul

4
bien meilleure réponse que la réponse acceptée! Merci
bois flotté

50

Vous devez remplacer hashCode () dans chaque classe qui remplace equals (). Ne pas le faire entraînera une violation du contrat général pour Object.hashCode (), ce qui empêchera votre classe de fonctionner correctement en conjonction avec toutes les collections basées sur le hachage, y compris HashMap, HashSet et Hashtable.


   de Effective Java , par Joshua Bloch

En définissant equals()et de manière hashCode()cohérente, vous pouvez améliorer la convivialité de vos classes en tant que clés dans les collections basées sur le hachage. Comme l'explique le document API pour hashCode: "Cette méthode est prise en charge au profit de tables de hachage telles que celles fournies par java.util.Hashtable."

La meilleure réponse à votre question sur la façon d'implémenter ces méthodes efficacement est de vous suggérer de lire le chapitre 3 de Java efficace .


4
C'est la bonne réponse. Le corollaire étant, bien sûr, que si vous n'utilisez jamais la classe dans une collection basée sur le hachage, alors peu importe que vous n'ayez pas implémenté hashCode().
mince

1
Dans des cas plus complexes, vous ne savez jamais si les collections que vous utilisez utilisent des hachages, alors restez à l'écart de "peu importe que vous n'ayez pas implémenté hashCode ()"
Victor Sergienko

1
Puis-je remplacer hashCode () sans remplacer equals ()?
Johnny

@StasS, oui, contrairement à ce que dit la réponse acceptée. Voir l'explication dans la deuxième partie de cet article: Pourquoi vous devriez toujours remplacer hashCode lors de la substitution égale
aioobe

22

Autrement dit, la méthode equals dans Object vérifie l'égalité de référence, où deux instances de votre classe peuvent toujours être sémantiquement égales lorsque les propriétés sont égales. C'est par exemple important lorsque vous placez vos objets dans un conteneur qui utilise equals et hashcode, comme HashMap et Set . Disons que nous avons une classe comme:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

Nous créons deux instances avec le même identifiant :

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Sans égal égal, nous obtenons:

  • a.equals (b) est faux car ce sont deux instances différentes
  • a.equals (a) est vrai car c'est la même instance
  • b.equals (b) est vrai car c'est la même instance

Correct? Eh bien peut-être, si c'est ce que vous voulez. Mais disons que nous voulons que les objets avec le même identifiant soient le même objet, qu'il s'agisse de deux instances différentes. Nous remplaçons les égaux (et le code de hachage):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

Quant à l'implémentation d'equals et de hashcode, je peux recommander d'utiliser les méthodes d'assistance de Guava


20

L'identité n'est pas l'égalité.

  • est égal à l'opérateur == identité du test de l' .
  • equals(Object obj) la méthode compare le test d'égalité (c'est-à-dire que nous devons dire l'égalité en remplaçant la méthode)

Pourquoi dois-je remplacer les méthodes equals et hashCode en Java?

Nous devons d'abord comprendre l'utilisation de la méthode des égaux.

Afin d'identifier les différences entre deux objets, nous devons remplacer la méthode des égaux.

Par exemple:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

Désormais, la méthode hashCode peut facilement comprendre.

hashCode produit un entier afin de stocker un objet dans des structures de données comme HashMap , HashSet .

Supposons que nous ayons la méthode override equals Customercomme ci-dessus,

customer1.equals(customer2);  // returns true by our own logic

Tout en travaillant avec la structure de données lorsque nous stockons l'objet dans des compartiments (le compartiment est un nom de fantaisie pour dossier) Si nous utilisons la technique de hachage intégrée, pour plus de deux clients, cela génère deux codes de hachage différents. Nous stockons donc le même objet identique à deux endroits différents. Pour éviter ce genre de problèmes, nous devons remplacer la méthode hashCode également basée sur les principes suivants.

  • les instances non égales peuvent avoir le même code de hachage.
  • des instances égales doivent retourner le même code de hachage.

3
C'est ce que je cherchais depuis 1 heure. Super compagnon (y)
Adnan

13

Ok, permettez-moi d'expliquer le concept en termes très simples.

Premièrement, dans une perspective plus large, nous avons des collections, et hashmap est l'une des infrastructures de données dans les collections.

Pour comprendre pourquoi nous devons remplacer les deux méthodes égal et hashcode, si besoin de comprendre d'abord ce qu'est hashmap et ce qui fait.

Une table de hachage est une structure de données qui stocke des paires clé-valeur de données à la manière d'un tableau. Disons a [], où chaque élément de 'a' est une paire de valeurs clés.

De plus, chaque index du tableau ci-dessus peut être une liste liée ayant ainsi plus d'une valeur à un index.

Maintenant, pourquoi une table de hachage est-elle utilisée? Si nous devons rechercher parmi un grand tableau, puis en les recherchant si elles ne seront pas efficaces, alors quelle technique de hachage nous dit qui permet de pré-traiter le tableau avec une logique et de regrouper les éléments en fonction de cette logique, c'est-à-dire le hachage

par exemple: nous avons le tableau 1,2,3,4,5,6,7,8,9,10,11 et nous appliquons une fonction de hachage mod 10 pour que 1,11 soit regroupé. Donc, si nous devions rechercher 11 dans le tableau précédent, nous devrions itérer le tableau complet, mais lorsque nous le groupons, nous limitons notre portée d'itération, améliorant ainsi la vitesse. Cette structure de données utilisée pour stocker toutes les informations ci-dessus peut être considérée comme un tableau 2D pour plus de simplicité

Maintenant, en dehors de la table de hachage ci-dessus, elle indique également qu'elle n'ajoutera aucun doublon. Et c'est la principale raison pour laquelle nous devons remplacer les égaux et le hashcode

Donc, quand on dit que cela explique le fonctionnement interne de hashmap, nous devons trouver quelles méthodes le hashmap a et comment suit-il les règles ci-dessus que j'ai expliquées ci-dessus

donc le hashmap a la méthode appelée as put (K, V), et selon hashmap il devrait suivre les règles ci-dessus pour distribuer efficacement le tableau et ne pas ajouter de doublons

donc ce qu'il fait, c'est qu'il va d'abord générer le code de hachage pour la clé donnée pour décider dans quel index la valeur doit entrer. si rien n'est présent à cet index, la nouvelle valeur sera ajoutée là-bas, si quelque chose est déjà là-bas alors la nouvelle valeur doit être ajoutée après la fin de la liste chaînée à cet index. mais n'oubliez pas qu'aucun doublon ne doit être ajouté selon le comportement souhaité de la table de hachage. disons donc que vous avez deux objets entiers aa = 11, bb = 11. comme chaque objet dérivé de la classe d'objets, l'implémentation par défaut pour comparer deux objets est qu'il compare la référence et non les valeurs à l'intérieur de l'objet. Donc, dans le cas ci-dessus, les deux, bien que sémantiquement égaux, échoueront au test d'égalité, et la possibilité que deux objets ayant le même code de hachage et les mêmes valeurs existent, créant ainsi des doublons. Si nous remplaçons alors nous pourrions éviter d'ajouter des doublons. Vous pouvez également vous référer àTravail de détail

import java.util.HashMap;


public class Employee {

String name;
String mobile;
public Employee(String name,String mobile) {
    this.name=name;
    this.mobile=mobile;
}

@Override
public int hashCode() {
    System.out.println("calling hascode method of Employee");
    String str=this.name;
    Integer sum=0;
    for(int i=0;i<str.length();i++){
        sum=sum+str.charAt(i);
    }
    return sum;

}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    System.out.println("calling equals method of Employee");
    Employee emp=(Employee)obj;
    if(this.mobile.equalsIgnoreCase(emp.mobile)){

        System.out.println("returning true");
        return true;
    }else{
        System.out.println("returning false");
        return false;
    }


}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    Employee emp=new Employee("abc", "hhh");
    Employee emp2=new Employee("abc", "hhh");
    HashMap<Employee, Employee> h=new HashMap<>();
    //for (int i=0;i<5;i++){
        h.put(emp, emp);
        h.put(emp2, emp2);

    //}

    System.out.println("----------------");
    System.out.println("size of hashmap: "+h.size());


}

}

J'ai une confusion, pourquoi avons-nous besoin de remplacer la méthode equals lorsque nous remplaçons la méthode hashCode dans le cas de HashMap? Dans tous les cas, hashmap remplace la valeur si le code de hachage de l'objet est égal.
Vikas Verma

@VikasVerma hashmap ne remplace aucune sorte de valeur si le hashcode des objets est égal, il décide seulement de l'index où le nouvel objet ajouté au hashmap doit être placé. Maintenant, il peut y avoir des objets à l'index, donc pour éviter les doublons, nous remplaçons la méthode des égaux et nous écrivons la logique pour définir quand les deux objets en comparaison doivent être traités comme égaux. S'il n'est pas remplacé, les objets ayant les mêmes valeurs seront stockés car la référence des deux objets sera différente
Chetan

11

hashCode() :

Si vous ne remplacez que la méthode du code de hachage, rien ne se passera. Parce qu'il retourne toujours new hashCodepour chaque objet en tant que classe Object.

equals() :

Si vous ne remplacez que la méthode égale, cela a.equals(b)est vrai, cela signifie que les éléments hashCodea et b doivent être identiques mais ne doivent pas se produire. Parce que tu n'as pas remplacéhashCode méthode.

Remarque: la hashCode()méthode de la classe Object renvoie toujours new hashCodepour chaque objet.

Donc, lorsque vous devez utiliser votre objet dans la collection basée sur le hachage, vous devez remplacer les deux equals() et hashCode().


C'est un point intéressant, à propos de remplacer uniquement hashCode () . C'est très bien, non? Ou peut-il y avoir aussi des cas problématiques?
Johnny

1
C'est une réponse trompeuse et erronée. Remplacer (= seulement =) hashCode () garantit que chaque objet qui est instancié de la classe respective avec des propriétés similaires porte le même code de hachage. Mais ne sera pas utile car aucun d'entre eux ne sera égal à l'autre.
mfaisalhyder

8

Java met une règle qui

"Si deux objets sont égaux en utilisant la méthode Object class equals, alors la méthode hashcode devrait donner la même valeur pour ces deux objets."

Donc, si dans notre classe nous remplaçons, equals()nous devons hashcode()également remplacer la méthode pour suivre cette règle. Les deux méthodes equals()et hashcode()sont utilisées Hashtable, par exemple, pour stocker des valeurs sous forme de paires clé-valeur. Si nous remplaçons l'un et pas l'autre, il est possible que le Hashtablene fonctionne pas comme nous le souhaitons, si nous utilisons un tel objet comme clé.


6

Parce que si vous ne les remplacez pas, vous utiliserez l'implantation par défaut dans Object.

Étant donné que l'égalité d'instance et les valeurs de hascode nécessitent généralement une connaissance de ce qui constitue un objet, elles devront généralement être redéfinies dans votre classe pour avoir une signification tangible.


6

Afin d'utiliser nos propres objets de classe comme clés dans des collections comme HashMap, Hashtable etc., nous devons remplacer les deux méthodes (hashCode () et equals ()) en ayant une connaissance du fonctionnement interne de la collection. Sinon, cela conduit à de mauvais résultats auxquels on ne s'attend pas.


6

Ajout à la réponse de @Lombo

Quand devrez-vous remplacer equals ()?

L'implémentation par défaut de equals () de Object est

public boolean equals(Object obj) {
        return (this == obj);
}

ce qui signifie que deux objets ne seront considérés comme égaux que s'ils ont la même adresse mémoire, ce qui ne sera vrai que si vous comparez un objet avec lui-même.

Mais vous voudrez peut-être considérer deux objets comme identiques s'ils ont la même valeur pour une ou plusieurs de leurs propriétés (reportez-vous à l'exemple donné dans la réponse de @Lombo).

Vous allez donc passer outre equals()dans ces situations et vous donneriez vos propres conditions d'égalité.

J'ai réussi à implémenter equals () et cela fonctionne très bien, alors pourquoi demandent-ils également de remplacer hashCode ()?

Eh bien. Tant que vous n'utilisez pas de collections basées sur "Hash" sur votre classe définie par l'utilisateur, c'est très bien. Mais dans le futur, vous voudrez peut-être utiliser HashMapou HashSetet si vous ne le faites pas overrideet "implémentez correctement" hashCode () , cette collection basée sur Hash ne fonctionnera pas comme prévu.

Remplacer uniquement égal (ajout à la réponse de @Lombo)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

Tout d'abord, HashMap vérifie si le hashCode de secondest le même que first. Seulement si les valeurs sont les mêmes, il procédera à la vérification de l'égalité dans le même compartiment.

Mais ici, le hashCode est différent pour ces 2 objets (car ils ont une adresse mémoire différente de l'implémentation par défaut). Par conséquent, il ne se souciera même pas de vérifier l'égalité.

Si vous avez un point d'arrêt dans votre méthode equals () remplacée, il n'interviendra pas s'ils ont des codes de hachage différents. contains()vérifie hashCode()et seulement si elles sont identiques, elle appellera votre equals()méthode.

Pourquoi ne pouvons-nous pas faire en sorte que HashMap vérifie l'égalité dans tous les compartiments? Il n'est donc pas nécessaire pour moi de remplacer hashCode () !!

Ensuite, vous manquez l'intérêt des collections basées sur Hash. Considérer ce qui suit :

Your hashCode() implementation : intObject%9.

Voici les clés stockées sous forme de compartiments.

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

Dites, vous voulez savoir si la carte contient la clé 10. Voulez-vous rechercher tous les compartiments? ou Souhaitez-vous rechercher un seul compartiment?

Sur la base du hashCode, vous identifieriez que si 10 est présent, il doit être présent dans le Bucket 1. Ainsi, seul le Bucket 1 sera recherché !!


5
class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put ('key', 'value') calculera la valeur de hachage en utilisant hashCode()pour déterminer le compartiment et utilise la equals()méthode pour trouver si la valeur est déjà présente dans le compartiment. Sinon, il sera ajouté sinon il sera remplacé par la valeur actuelle
  • get ('key') utilisera hashCode()pour trouver l'entrée (compartiment) en premier et equals()pour trouver la valeur dans Entry

si les deux sont remplacés,

Carte < A >

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

si égal n'est pas remplacé

Carte < A >

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

Si hashCode n'est pas remplacé

Carte < A >

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

Contrat égal HashCode

  1. Deux clés égales selon la même méthode devraient générer le même hashCode
  2. Deux clés générant le même hashCode n'ont pas besoin d'être égales (dans l'exemple ci-dessus, tous les nombres pairs génèrent le même code de hachage)

4

Considérez la collecte de balles dans un seau tout en couleur noire. Votre travail consiste à colorer ces balles comme suit et à les utiliser pour le jeu approprié,

Pour le tennis - jaune, rouge. Pour le cricket - blanc

Maintenant, le seau a des boules en trois couleurs jaune, rouge et blanc. Et que maintenant vous avez fait la coloration Vous seul savez quelle couleur est pour quel jeu.

Coloration des boules - Hachage. Choisir la balle pour le jeu - Égale.

Si vous avez fait la coloration et que quelqu'un choisit la balle pour le cricket ou le tennis, ils ne se soucieront pas de la couleur !!!


4

Je cherchais l'explication "Si vous ne remplacez que hashCode, lorsque vous l'appelez, myMap.put(first,someValue)il prend d'abord, calcule son hashCode et le stocke dans un compartiment donné. Ensuite, lorsque vous appelezmyMap.put(first,someOtherValue) il doit remplacer d'abord par second selon la documentation de la carte car ils sont égaux (selon notre définition). " :

Je pense que la 2ème fois quand nous ajoutons myMapalors ce devrait être le «deuxième» objet commemyMap.put(second,someOtherValue)


4

1) L'erreur courante est illustrée dans l'exemple ci-dessous.

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

la voiture verte est introuvable

2. Problème provoqué par hashCode ()

Le problème est dû à la méthode non remplacée hashCode(). Le contrat entre equals()et hashCode()est:

  1. Si deux objets sont égaux, ils doivent avoir le même code de hachage.
  2. Si deux objets ont le même code de hachage, ils peuvent ou non être égaux.

    public int hashCode(){  
      return this.color.hashCode(); 
    }

4

Il est utile lors de l'utilisation d' objets de valeur . Ce qui suit est un extrait du Portland Pattern Repository :

Des exemples d'objets de valeur sont des choses comme les nombres, les dates, l'argent et les chaînes. Ce sont généralement de petits objets qui sont largement utilisés. Leur identité est basée sur leur état plutôt que sur leur identité d'objet. De cette façon, vous pouvez avoir plusieurs copies du même objet de valeur conceptuelle.

Je peux donc avoir plusieurs copies d'un objet qui représente la date du 16 janvier 1998. Chacune de ces copies sera égale l'une à l'autre. Pour un petit objet tel que celui-ci, il est souvent plus facile d'en créer de nouveaux et de les déplacer plutôt que de compter sur un seul objet pour représenter la date.

Un objet de valeur doit toujours remplacer .equals () en Java (ou = dans Smalltalk). (N'oubliez pas de remplacer également .hashCode ().)


3

Supposons que vous ayez la classe (A) qui agrège deux autres (B) (C) et que vous devez stocker des instances de (A) dans la table de hachage. L'implémentation par défaut permet uniquement de distinguer les instances, mais pas par (B) et (C). Donc, deux instances de A pourraient être égales, mais par défaut ne vous permettrait pas de les comparer de manière correcte.


3

Les méthodes equals et hashcode sont définies dans la classe d'objets. Par défaut, si la méthode equals renvoie true, le système ira plus loin et vérifiera la valeur du code de hachage. Si le code de hachage des 2 objets est également le même, alors les objets seront considérés comme identiques. Donc, si vous remplacez uniquement la méthode égal, alors même si la méthode remplacée est égale à 2 objets égaux, le code de hachage défini par le système peut ne pas indiquer que les 2 objets sont égaux. Nous devons donc également remplacer le code de hachage.


Si la méthode equals renvoie true, il n'est pas nécessaire de vérifier le code de hachage. Cependant, si deux objets ont des codes de hachage différents, il faut pouvoir les considérer comme différents sans avoir à appeler égaux. De plus, la connaissance qu'aucune des choses sur une liste n'a un code de hachage particulier implique qu'aucune des choses sur la liste ne peut correspondre à aucun objet avec ce code de hachage. À titre d'exemple simple, si l'on a une liste d'objets dont les codes de hachage sont des nombres pairs et une liste d'objets où ils sont des nombres impairs, aucun objet dont le code de hachage est un nombre pair ne sera dans la deuxième liste.
supercat

Si l'on avait deux objets X et Y dont les méthodes "égales" indiquaient qu'ils correspondaient, mais le code de hachage de X était un nombre pair et le code de hachage de Y était un nombre impair, une collection comme décrit ci-dessus qui a noté que le code de hachage de l'objet Y était impair et stocké il sur la deuxième liste ne serait pas en mesure de trouver une correspondance pour l'objet X. Il observerait que le code de hachage de X était pair, et puisque la deuxième liste n'a pas d'objets avec des codes de hachage de numéro pair, cela ne dérangerait pas pour y rechercher quelque chose qui correspond à X, même si Y correspondrait à X. Ce que vous devez dire ...
supercat

... serait que de nombreuses collections éviteront de comparer des choses dont les codes de hachage impliqueraient qu'ils ne peuvent pas être égaux. Étant donné deux objets dont les codes de hachage sont inconnus, il est souvent plus rapide de les comparer directement que de calculer leurs codes de hachage, il n'y a donc aucune garantie que les choses qui signalent des codes de hachage inégaux mais retournent truepour equalsne seront pas considérées comme correspondant. D'un autre côté, si les collections se produisent, notez que les choses ne peuvent pas avoir le même code de hachage, elles ne remarqueront probablement pas qu'elles sont égales.
supercat

3

Equals et méthodes Hashcode en Java

Ce sont des méthodes de la classe java.lang.Object qui est la super classe de toutes les classes (classes personnalisées également et autres définies dans l'API java).

La mise en oeuvre:

public boolean equals (Object obj)

public int hashCode ()

entrez la description de l'image ici

public boolean equals (Object obj)

Cette méthode vérifie simplement si deux références d'objet x et y font référence au même objet. c'est à dire qu'il vérifie si x == y.

Il est réflexif: pour toute valeur de référence x, x.equals (x) doit renvoyer true.

Il est symétrique: pour toutes les valeurs de référence x et y, x.equals (y) doit retourner vrai si et seulement si y.equals (x) retourne vrai.

Il est transitif: pour toutes les valeurs de référence x, y et z, si x.equals (y) retourne vrai et y.equals (z) retourne vrai, alors x.equals (z) doit retourner vrai.

Il est cohérent: pour toutes les valeurs de référence x et y, plusieurs invocations de x.equals (y) retournent systématiquement vrai ou retournent systématiquement faux, à condition qu'aucune information utilisée dans les comparaisons égales sur l'objet ne soit modifiée.

Pour toute valeur de référence non nulle x, x.equals (null) doit renvoyer false.

public int hashCode ()

Cette méthode renvoie la valeur du code de hachage pour l'objet sur lequel cette méthode est invoquée. Cette méthode renvoie la valeur du code de hachage sous forme d'entier et est prise en charge au profit des classes de collecte basées sur le hachage telles que Hashtable, HashMap, HashSet etc. Cette méthode doit être remplacée dans chaque classe qui remplace la méthode equals.

Le contrat général de hashCode est:

Chaque fois qu'elle est invoquée plusieurs fois sur le même objet lors de l'exécution d'une application Java, la méthode hashCode doit toujours renvoyer le même entier, à condition qu'aucune information utilisée dans des comparaisons égales sur l'objet ne soit modifiée.

Cet entier n'a pas besoin de rester cohérent d'une exécution d'une application à une autre exécution de la même application.

Si deux objets sont égaux selon la méthode equals (Object), l'appel de la méthode hashCode sur chacun des deux objets doit produire le même résultat entier.

Il n'est pas nécessaire que si deux objets sont inégaux selon la méthode equals (java.lang.Object), alors appeler la méthode hashCode sur chacun des deux objets doit produire des résultats entiers distincts. Cependant, le programmeur doit être conscient que la production de résultats entiers distincts pour des objets inégaux peut améliorer les performances des tables de hachage.

Les objets égaux doivent produire le même code de hachage tant qu'ils sont égaux, mais les objets inégaux n'ont pas besoin de produire des codes de hachage distincts.

Ressources:

JavaRanch

Image


L'image (lien vidéo) est en mode privé. Rendez-le public à regarder.
UdayKiran Pulipati

2

Dans l'exemple ci-dessous, si vous commentez la substitution pour égal ou hashcode dans la classe Personne, ce code ne parviendra pas à rechercher la commande de Tom. L'utilisation de l'implémentation par défaut de hashcode peut entraîner des échecs dans les recherches de table de hachage.

Ce que j'ai ci-dessous est un code simplifié qui extrait l'ordre des personnes par personne. La personne est utilisée comme clé dans la table de hachage.

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}

2

La classe String et les classes wrapper ont une implémentation différente de equals()ethashCode() méthodes différentes de la classe Object. La méthode equals () de la classe Object compare les références des objets, pas le contenu. La méthode hashCode () de la classe Object renvoie un code de hachage distinct pour chaque objet, que le contenu soit le même.

Cela entraîne un problème lorsque vous utilisez la collection de cartes et que la clé est de type Persistant, de type StringBuffer / builder. Puisqu'ils ne remplacent pas equals () et hashCode () contrairement à la classe String, equals () retournera false lorsque vous comparez deux objets différents même si les deux ont le même contenu. Cela rendra le hashMap stockant les mêmes clés de contenu. Le stockage des mêmes clés de contenu signifie qu'il enfreint la règle de Map car Map n'autorise pas du tout les clés en double. Par conséquent, vous remplacez les méthodes equals () ainsi que hashCode () dans votre classe et fournissez l'implémentation (l'EDI peut générer ces méthodes) afin qu'elles fonctionnent de la même manière que String's equals () et hashCode () et empêchent les mêmes clés de contenu.

Vous devez remplacer la méthode hashCode () avec equals () car equals () fonctionne selon hashcode.

De plus, le remplacement de la méthode hashCode () avec equals () permet de conserver le contrat equals () - hashCode (): "Si deux objets sont égaux, ils doivent avoir le même code de hachage."

Quand avez-vous besoin d'écrire une implémentation personnalisée pour hashCode ()?

Comme nous le savons, le fonctionnement interne de HashMap est basé sur le principe du hachage. Il existe certains compartiments où les entrées sont stockées. Vous personnalisez l'implémentation de hashCode () en fonction de vos besoins afin que les mêmes objets de catégorie puissent être stockés dans le même index. lorsque vous stockez les valeurs dans la collection Map à l'aide de la put(k,v)méthode, l'implémentation interne de put () est:

put(k, v){
hash(k);
index=hash & (n-1);
}

Cela signifie qu'il génère un index et que l'index est généré en fonction du code de hachage d'un objet clé particulier. Donc, faites en sorte que cette méthode génère un code de hachage selon vos besoins, car les mêmes ensembles d'entrées de code de hachage seront stockés dans le même compartiment ou index.

C'est ça!


1

hashCode()est utilisée pour obtenir un entier unique pour un objet donné. Cet entier est utilisé pour déterminer l'emplacement du compartiment, lorsque cet objet doit être stocké dans certains HashTable, HashMapcomme la structure de données. Par défaut, la hashCode()méthode Object renvoie et une représentation entière de l'adresse mémoire où l'objet est stocké.

La hashCode()méthode des objets est utilisée lorsque nous les insérons dans un HashTable, HashMapou HashSet. Plus d'informations HashTablessur Wikipedia.org pour référence.

Pour insérer une entrée dans la structure des données cartographiques, nous avons besoin à la fois de la clé et de la valeur. Si la clé et les valeurs sont des types de données définis par l'utilisateur, hashCode()la clé déterminera où stocker l'objet en interne. Lorsque vous devez également rechercher l'objet à partir de la carte, le code de hachage de la clé déterminera où rechercher l'objet.

Le code de hachage pointe uniquement vers une certaine "zone" (ou liste, compartiment, etc.) en interne. Étant donné que différents objets clés pourraient potentiellement avoir le même code de hachage, le code de hachage lui-même ne garantit pas que la bonne clé est trouvée. Le HashTableitère alors cette zone (toutes les clés avec le même code de hachage) et utilise la clé de equals()la méthode pour trouver la bonne clé. Une fois la bonne clé trouvée, l'objet stocké pour cette clé est renvoyé.

Ainsi, comme nous pouvons le voir, une combinaison des méthodes hashCode()et equals()est utilisée lors du stockage et de la recherche d'objets dans a HashTable.

REMARQUES:

  1. Utilisez toujours les mêmes attributs d'un objet pour générer hashCode()et les equals()deux. Comme dans notre cas, nous avons utilisé l'ID d'employé.

  2. equals() doit être cohérent (si les objets ne sont pas modifiés, il doit continuer à renvoyer la même valeur).

  3. Chaque fois a.equals(b), alors a.hashCode()doit être le même que b.hashCode().

  4. Si vous remplacez l'un, vous devez remplacer l'autre.

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html


hashCode()n'est pas utilisé pour renvoyer un entier unique pour chaque objet. C'est impossible. Vous l'avez contredit vous-même dans la deuxième phrase du quatrième paragraphe.
Marquis de Lorne

@EJP, la plupart du temps, hascode () renverra l'entier unique pour deux objets différents. Mais il y aura des chances de collision de hascode pour deux objets différents, ce concept est appelé Hashcode Collision . Veuillez vous référer à: tech.queryhome.com/96931/…
Paramesh Korrakuti

1

À mon humble avis, c'est selon la règle - Si deux objets sont égaux, ils doivent avoir le même hachage, c'est-à-dire que des objets égaux doivent produire des valeurs de hachage égales.

Étant donné ci-dessus, égal à () par défaut dans Object est == qui fait une comparaison sur l'adresse, hashCode () renvoie l'adresse en entier (hachage sur l'adresse réelle) qui est à nouveau distinct pour Object distinct.

Si vous devez utiliser les objets personnalisés dans les collections basées sur Hash, vous devez remplacer à la fois equals () et hashCode (). Je peux finir par remplacer les deux objets employés différents, cela se produit lorsque j'utilise l'âge comme hashCode (), mais je devrais utiliser la valeur unique qui peut être l'ID employé.


1

Pour vous aider à vérifier les objets en double, nous avons besoin d'un égal personnalisé et d'un hashCode.

Puisque le code de hachage renvoie toujours un nombre, il est toujours rapide de récupérer un objet en utilisant un nombre plutôt qu'une clé alphabétique. Comment ça va? Supposons que nous avons créé un nouvel objet en passant une valeur qui est déjà disponible dans un autre objet. Maintenant, le nouvel objet renverra la même valeur de hachage que celle d'un autre objet car la valeur transmise est la même. Une fois la même valeur de hachage renvoyée, la JVM ira à la même adresse mémoire à chaque fois et si, dans le cas où plusieurs objets sont présents pour la même valeur de hachage, elle utilisera la méthode equals () pour identifier l'objet correct.


1

Lorsque vous souhaitez stocker et récupérer votre objet personnalisé en tant que clé dans Map, vous devez toujours remplacer equals et hashCode dans votre objet personnalisé. Par exemple:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

Ici, p1 et p2 considéreront comme un seul objet et la maptaille ne sera que de 1 car ils sont égaux.


1
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

Classe de test

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

Dans Object Class equals (Object obj) est utilisé pour comparer la comparaison d'adresses, c'est pourquoi lorsque dans la classe Test, si vous comparez deux objets, cela équivaut à la méthode donnant false, mais lorsque nous remplaçons hashcode (), il peut comparer le contenu et donner un résultat correct.


et classe d'essai j'ai ajouté dans le programme ci-dessous.
Manash Ranjan Dakua

Dans Object Class equals (Object obj) est utilisé pour comparer la comparaison d'adresses, c'est pourquoi lorsque dans la classe Test, si vous comparez deux objets, cela équivaut à la méthode donnant false, mais lorsque nous remplaçons hashcode (), il peut comparer le contenu et donner un résultat correct.
Manash Ranjan Dakua

1
vous pouvez utiliser le lien d'édition juste en dessous de cette réponse pour l'ajouter à votre réponse .. Veuillez ne pas ajouter de réponse comme deux réponses incomplètes
Suraj Rao

1

Si vous remplacez equals()et non hashcode(), vous ne trouverez aucun problème à moins que vous ou quelqu'un d'autre n'utilisiez ce type de classe dans une collection hachée comme HashSet. Les gens avant moi ont clairement expliqué la théorie documentée à plusieurs reprises, je suis juste ici pour fournir un exemple très simple.

Considérons une classe dont le equals()besoin de signifier quelque chose de personnalisé: -

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

Considérez maintenant cette classe principale: -

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

Cela produira la sortie suivante: -

    true
    -----------------------------------
    true
    -----------------------------------
    1

Je suis content des résultats. Mais si je ne l'ai pas remplacé hashCode(), cela provoquera un cauchemar car les objets Rishavavec le même contenu de membre ne seront plus traités comme uniques car hashCodeils seront différents, comme généré par le comportement par défaut, voici la sortie: -

    true
    -----------------------------------
    false
    -----------------------------------
    2

0

Les deux méthodes sont définies dans la classe Object. Et les deux sont dans sa mise en œuvre la plus simple. Donc, lorsque vous en avez besoin, vous souhaitez ajouter un peu plus d'implémentation à ces méthodes, vous devez remplacer dans votre classe.

Pour Ex: la méthode equals () dans l'objet vérifie uniquement son égalité sur la référence. Donc, si vous devez également comparer son état, vous pouvez le remplacer comme cela se fait dans la classe String.


-3

Bah - "Vous devez remplacer hashCode () dans chaque classe qui remplace equals ()."

[extrait de Effective Java, par Joshua Bloch?]

N'est-ce pas le contraire? Le remplacement de hashCode implique probablement que vous écrivez une classe de clé de hachage, mais le remplacement de equals ne le fait certainement pas. Il existe de nombreuses classes qui ne sont pas utilisées comme clés de hachage, mais qui souhaitent une méthode de test d'égalité logique pour une autre raison. Si vous choisissez «égal» pour cela, vous pouvez alors être mandaté pour écrire une implémentation de hashCode par application trop zélée de cette règle. Tout ce qui réussit est d'ajouter du code non testé dans la base de code, un mal qui attend de trébucher dans le futur. Également, écrire du code dont vous n'avez pas besoin est anti-agile. C'est juste faux (et une idée générée sera probablement incompatible avec vos égaux fabriqués à la main).

Ils auraient sûrement dû mandater une interface sur des objets écrits pour être utilisés comme clés? Quoi qu'il en soit, Object n'aurait jamais dû fournir par défaut hashCode () et equals () imho. Cela encourage probablement de nombreuses collections de hachage cassées.

Mais de toute façon, je pense que la "règle" est écrite à l'envers. En attendant, je continuerai à éviter d'utiliser "égal" pour les méthodes de test d'égalité :-(

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.