Récemment, j'ai lu ceci Developer Works Document .
Le document a pour but de définir les fonctions hashCode()
et equals()
de manière efficace et correcte, mais je ne suis pas en mesure de comprendre pourquoi nous devons remplacer ces deux méthodes.
Comment puis-je prendre la décision de mettre en œuvre ces méthodes efficacement?
Joshua Bloch dit sur Effective Java
Vous devez surcharger hashCode () dans chaque classe qui annule equals (). Si vous ne le faites pas, cela entraînera une violation du contrat général pour Object.hashCode (), ce qui empêchera votre classe de fonctionner correctement avec toutes les collections basées sur un hachage, y compris HashMap, HashSet et Hashtable.
Essayons de le comprendre avec un exemple de ce qui se produirait si nous surchargions equals()
sans surcharger hashCode()
et essayions d'utiliser une variable Map
.
Disons que nous avons une classe comme celle-ci et que deux objets de MyClass
sont égaux si leur importantField
est égale (avec hashCode()
et equals()
générés par Eclipse)
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;
}
}
Ne remplace que equals
Si seulement equals
est remplacé, alors, lorsque vous appelez myMap.put(first,someValue)
, un hachage est créé dans un compartiment et lorsque vous appelez myMap.put(second,someOtherValue)
, il est répercuté sur un autre compartiment (car ils ont une hashCode
différente). Ainsi, bien qu'ils soient égaux, comme ils ne partagent pas le même seau, 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 substituons hashCode()
, voyons ce qui se produirait dans ce cas particulier où nous savons que deux objets de MyClass
sont égaux si leur importantField
est égal mais nous ne remplaçons pas equals()
.
Ne remplace que hashCode
Imaginez que vous avez ceci
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
Si vous ne remplacez que hashCode
, alors, lorsque vous appelez myMap.put(first,someValue)
, il commence par calculer, calcule sa hashCode
et la stocke dans un compartiment donné. Ensuite, lorsque vous appelez myMap.put(second,someOtherValue)
, il convient de remplacer le premier par le suivant, conformément à la Documentation de la carte car ils sont égaux (selon les besoins de l’entreprise).
Mais le problème est que l’égalité n’a pas été redéfinie. Ainsi, lorsque la carte hache la variable second
et effectue une itération dans le compartiment, il suffit de rechercher un objet k
tel que second.equals(k)
soit vrai, il n’en trouvera pas car second.equals(first)
sera false
.
J'espère que c'était clair
Les collections telles que HashMap
et HashSet
utilisent un hashcode valeur d'un objet pour déterminer comment il doit être stocké dans une collection, et le hashcode est utilisé à nouveau pour localiser l'objet dans sa collection .
La récupération du hachage est un processus en deux étapes:
hashCode()
)equals()
)Voici un petit exemple expliquant pourquoi nous devrions remplacer equals()
et hashcode()
.
Considérons une classe Employee
qui a deux champs: age et name.
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 objet Employee
dans une HashSet
et 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 ce qui suit:
false
employee.hashCode(): 321755204 employee2.hashCode():375890482
Maintenant décommentez la méthode hashcode()
, exécutez la même chose et le résultat serait:
true
employee.hashCode(): -938387308 employee2.hashCode():-938387308
Vous pouvez maintenant voir pourquoi, si deux objets sont considérés égaux, leur hashcode s doit également être égal à? Sinon, vous ne pourrez plus jamais trouver l’objet car la valeur par défaut hashcode méthode dans la classe Object génère presque toujours un numéro unique Pour chaque objet, même si la méthode equals()
est remplacée dans de telle sorte que deux objets ou plus soient considérés égaux. Peu importe l’égalité des objets si Leur hashcode s ne reflète pas cela. Encore une fois: si deux objets sont égaux, leur hashcode s doit également l'être.
Vous devez surcharger hashCode () dans chaque classe qui annule equals (). Échec cela entraînerait une violation de le contrat général pour Object.hashCode (), ce qui empêchera votre classe de fonctionner correctement en conjonction avec tous collections, y compris HashMap, HashSet et Hashtable.
from Effective Java, de Joshua Bloch
En définissant equals()
et hashCode()
de manière cohérente, vous pouvez améliorer la convivialité de vos classes en tant que clés dans les collections basées sur un hachage. Comme l'explique la documentation de l'API pour hashCode: "Cette méthode est prise en charge au profit des hashtables telles que celles fournies par Java.util.Hashtable
."
La meilleure réponse à votre question sur la façon de mettre en œuvre efficacement ces méthodes vous suggère de lire le chapitre 3 de Effective Java .
En termes simples, la méthode equals de la vérification d'objet pour l'égalité des références, où deux instances de votre classe pourraient toujours être sémantiquement égales lorsque les propriétés sont égales. Ceci 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 id:
Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");
Sans égaux, nous obtenons:
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 ou non de deux instances différentes. Nous remplaçons les égaux (et hashcode):
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();
}
}
Pour implémenter equals et hashcode, je peux vous recommander d’utiliser les méthodes d’aide de Guava
L'identité n'est pas l'égalité.
==
.equals(Object obj)
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 redéfinir la méthode equals.
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
Maintenant, la méthode hashCode peut comprendre facilement.
hashCode produit un entier pour stocker un objet dans des structures de données telles que HashMap , HashSet .
Supposons que nous ayons surchargé la méthode égale de Customer
comme ci-dessus,
customer1.equals(customer2); // returns true by our own logic
Tout en travaillant avec la structure de données lorsque nous stockons des objets dans des compartiments (le compartiment est un nom de fantaisie pour le dossier). Si nous utilisons la technique de hachage intégrée, elle génère deux codes de hachage différents pour les deux clients précédents. Nous stockons donc le même objet identique à deux endroits différents. Pour éviter ce genre de problème, nous devons remplacer la méthode hashCode également sur la base des principes suivants.
Ok, laissez-moi vous expliquer le concept avec des mots très simples.
D'abord, dans une perspective plus large, nous avons des collections, et hashmap est l'une des structures de données dans les collections.
Pour comprendre pourquoi nous devons redéfinir les méthodes equals et hashcode, nous devons d’abord comprendre ce qui est hashmap et ce qu’il fait.
Un hashmap est une structure de données qui stocke des paires de valeurs de données clés sous forme de tableau. Disons un [], où chaque élément de 'a' est une paire clé-valeur.
De plus, chaque index du tableau ci-dessus peut être une liste chaînée, ce qui donne plusieurs valeurs à un index.
Maintenant, pourquoi utilise-t-on une table de hachage? Si nous devons chercher dans un grand tableau, puis chercher dans chacun d’eux si leur efficacité est réduite, c’est pourquoi la technique de hachage nous permet de pré-traiter le tableau avec une certaine logique et de regrouper les éléments sur cette logique à savoir le hachage
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 afin 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 regroupons, nous limitons notre étendue d'itération, ce qui améliore 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, mis à part le hashmap ci-dessus, il est également indiqué qu'il n'y ajoutera pas de doublons. Et c’est la raison principale 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 a le hashmap et comment il suit les règles ci-dessus que j'ai expliquées ci-dessus
la méthode hashmap est donc appelée put (K, V) et, conformément à hashmap, elle doit suivre les règles ci-dessus pour distribuer efficacement le tableau et ne pas ajouter de doublons.
donc, ce qui est fait, c'est qu'il va d'abord générer le hashcode pour la clé donnée afin de décider 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à présent là-bas alors la nouvelle valeur devrait être ajoutée après la fin de la liste liée à cet index. mais rappelez-vous, aucun doublon ne doit être ajouté selon le comportement souhaité du hashmap. alors disons que vous avez deux objets Integer aa = 11, bb = 11 . comme chaque objet dérivé de la classe d'objet, 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. Ainsi, dans le cas ci-dessus, le test d’égalité échouera, même s’il est sémantiquement égal, et la possibilité que deux objets ayant le même hashcode et les mêmes valeurs existent, créant ainsi des doublons. Si nous ignorons alors nous pourrions éviter d’ajouter des doublons . Vous pouvez également vous référer à Travail détaillé
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());
}
}
hashCode()
:
Si vous ne remplacez que la méthode hash-code, rien ne se passera. Parce qu'il renvoie toujours new hashCode
pour chaque objet en tant que classe Object.
equals()
:
Si vous ne substituez que la méthode égale, a.equals(b)
est true, cela signifie que hashCode
de a et b doit être identique mais ne doit pas se produire. Parce que vous n'avez pas remplacé la méthode hashCode
.
Remarque: La méthode hashCode()
de la classe Object renvoie toujours le nouveau hashCode
pour chaque objet.
Ainsi, lorsque vous devez utiliser votre objet dans la collection basée sur le hachage, vous devez redéfinir les noms equals()
et hashCode()
.
Afin d'utiliser nos propres objets de classe comme clés dans des collections telles que HashMap, Hashtable, etc., nous devons remplacer les deux méthodes (hashCode () et equals ()) en prenant conscience du fonctionnement interne de la collection. Sinon, cela conduit à des résultats erronés auxquels nous ne sommes pas attendus.
Parce que si vous ne les remplacez pas, vous utiliserez l'implémentation par défaut dans Object.
Étant donné que l'égalité d'instance et les valeurs de code has nécessitent généralement la connaissance de la composition d'un objet, elles devront généralement être redéfinies dans votre classe pour avoir une signification concrète.
class A {
int i;
// Hashing Algorithm
if even number return 0 else return 1
// Equals Algorithm,
if i = this.i return true else false
}
hashCode()
pour déterminer le bucket et en utilisant la méthode equals()
pour déterminer si la valeur est déjà présente dans le seau. Sinon, il sera ajouté sinon il sera remplacé par la valeur actuellehashCode()
pour trouver l'entrée (compartiment) en premier et equals()
pour trouver la valeur dans l'entréesi 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...
HashCode Equal Contract
Java met une règle qui
"Si deux objets sont égaux à l'aide de la méthode Object class equals, la méthode hashcode devrait donner la même valeur pour ces deux objets."
Donc, si dans notre classe nous substituons equals()
nous devrions surcharger la méthode hashcode()
également pour suivre cette règle . Les deux méthodes equals()
et hashcode()
sont utilisées dans 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 la variable Hashtable
ne fonctionne pas comme nous le souhaitons, si nous utilisons un tel objet comme clé.
Considérez la collection 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 balles en trois couleurs: jaune, rouge et blanc. Et que maintenant vous avez fait la coloration Seulement vous savez quelle couleur est pour quel jeu.
Colorer les balles - Hashing . Choisir la balle pour le jeu - Equals.
Si vous faites la coloration et que quelqu'un choisit la balle pour le cricket ou le tennis, cela ne gêne pas la couleur !!!
Je cherchais l'explication suivante: "Si vous ne remplacez que hashCode, l'appelez myMap.put(first,someValue)
il prend d'abord, calcule son hashCode et le stocke dans un compartiment donné. Ensuite, lorsque vous appelez myMap.put(first,someOtherValue)
, il doit remplacer en premier par second selon la documentation cartographique sont égaux (selon notre définition). " :
Je pense que la 2ème fois que nous ajoutons dans myMap
, alors cela devrait être le 'deuxième' objet comme myMap.put(second,someOtherValue)
Méthodes Equals et 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 et autres définies dans l'API Java).
La mise en oeuvre:
public boolean equals (Object obj)
public int hashCode ()
public boolean equals (Object obj)
Cette méthode vérifie simplement si deux références d'objet x et y se rapportent au même objet. c'est-à-dire qu'il vérifie si x == y.
C'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 renvoyer true si et seulement si y.equals (x) renvoie true.
Il est transitif: pour toutes les valeurs de référence x, y et z, si x.equals (y) renvoie true et que y.equals (z) renvoie true, alors x.equals (z) doit renvoyer true.
Il est cohérent: pour toutes les valeurs de référence x et y, les invocations multiples de x.equals (y) retournent systématiquement true ou systématiquement false, à condition qu'aucune des informations utilisées dans les comparaisons égales de l'objet ne soit modifiée.
Pour toute valeur de référence non nulle x, x.equals (null) doit renvoyer faux.
public int hashCode ()
Cette méthode retourne la valeur de code de hachage pour l'objet sur lequel cette méthode est appelée. Cette méthode retourne la valeur du code de hachage sous forme d'entier et est prise en charge pour l'avantage des classes de collection basées sur le hachage telles que Hashtable, HashMap, HashSet, etc. Cette méthode doit être remplacée dans chaque classe remplaçant 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 systématiquement renvoyer le même entier, à condition qu'aucune information utilisée dans les comparaisons égales de 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 soient inégaux selon la méthode equals (Java.lang.Object), l'appel de la méthode hashCode sur chacun des deux objets doit produire des résultats entiers distincts. Cependant, le programmeur doit savoir que la production de résultats entiers distincts pour des objets inégaux peut améliorer les performances des hashtables.
Les objets identiques doivent produire le même code de hachage tant qu'ils sont égaux, cependant, des objets inégaux ne doivent pas nécessairement produire des codes de hachage distincts.
Ressources:
Les méthodes equals et hashcode sont définis dans la classe d'objet. 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 identique, alors les objets seront considérés comme identiques. Ainsi, si vous ne substituez que la méthode equals, alors même si la méthode equals remplacée indique que 2 objets sont égaux, le hashcode 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.
Supposons que la classe (A) regroupe deux autres (B) (C) et que vous ayez besoin de stocker des instances de (A) dans une table de hachage. L'implémentation par défaut permet uniquement de distinguer les instances, mais pas avec (B) et (C). Donc, deux instances de A pourraient être égales, mais défaut ne vous permettrait pas de les comparer correctement.
C'est utile lorsque vous utilisez Objets valeur . Ce qui suit est un extrait du Portland Pattern Repository :
Des exemples d'objets de valeur sont des choses comme des nombres, des dates, de l'argent et des cordes. Habituellement, ils sont petits objets qui sont utilisés assez largement . 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.
Donc, je peux avoir plusieurs copies d'un objet qui représente la date du 16 janvier 1998. Chacune de ces copies sera égale à l'autre. Pour un petit objet tel que celui-ci, il est souvent plus facile de créer de nouveaux et de déplacer autour d'eux plutôt que de compter sur un objet unique pour représenter la date.
Un objet de valeur doit toujours remplacer .equals () en Java (ou = en Smalltalk) . (N'oubliez pas de remplacer .hashCode () par Well.)
La classe String et les classes wrapper ont une implémentation différente des méthodes equals()
et hashCode()
par rapport à la classe Object. La méthode equals () de la classe Object compare les références des objets, pas leur contenu. La méthode hashCode () de la classe Object renvoie un hashcode distinct pour chaque objet, que le contenu soit identique.
Cela pose problème lorsque vous utilisez Collection de cartes et que la clé est de type Persistent, de type StringBuffer/Builder. Comme ils ne remplacent pas equals () et hashCode (), contrairement à la classe String, equals () renverra false lorsque vous comparez deux objets différents, même s'ils ont le même contenu. HashMap stockera les mêmes clés de contenu. Stocker les mêmes clés de contenu signifie qu'il enfreint la règle de Map car Map n'autorise pas la duplication de clés . Par conséquent, vous substituez les méthodes equals () ainsi que hashCode () dans votre classe et fournissez l'implémentation (l'IDE peut générer ces méthodes) ) de sorte qu'ils fonctionnent de la même manière que equals () et hashCode () de String et empêchent les mêmes clés de contenu.
Vous devez redéfinir la méthode hashCode () avec equals (), car equals () fonctionne selon hashcode.
De plus, surcharger la méthode hashCode () avec equals () aide à préserver 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 de hachage. Il y a certains compartiments où les ensembles d'entrées sont stockés. Vous personnalisez l'implémentation 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 put(k,v)
method, l'implémentation interne de put () est la suivante:
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é sur la base du hashcode d'un objet clé particulier. Assurez-vous donc que cette méthode génère un hashcode en fonction de vos besoins, car les mêmes ensembles d'entrées hashcode seront stockés dans le même compartiment ou index.
C'est tout!
1) L'erreur commune 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 causé par hashCode ()
Le problème est dû à la méthode non remplacée hashCode()
. Le contrat entre equals()
et hashCode()
est le suivant:
Si deux objets ont le même code de hachage, ils peuvent être égaux ou non.
public int hashCode(){
return this.color.hashCode();
}
Dans l'exemple ci-dessous, si vous commentez le remplacement pour Equals ou hashcode dans la classe Person, ce code ne pourra pas rechercher la commande de Tom. L'utilisation de l'implémentation par défaut de hashcode peut entraîner des erreurs dans les recherches de hashtable.
Ce que j'ai ci-dessous est un code simplifié qui établit 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);
}
}
}
IMHO, c'est comme par la règle dit - Si deux objets sont égaux, alors ils devraient avoir le même hachage, c'est-à-dire, des objets égaux devraient produire des valeurs de hachage égales.
Comme indiqué ci-dessus, equals () dans Object correspond à ==, qui effectue une comparaison d'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 le hachage, vous devez redéfinir equals () et hashCode (), par exemple Si je souhaite conserver le hachage des objets employés, si je n'utilise pas hashCode plus puissant Je peux finir par remplacer les deux objets employés différents, ce qui se produit lorsque j'utilise l'âge comme hashCode (), mais je devrais utiliser la valeur unique qui peut être l'ID d'employé.
La méthode 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 une structure de données semblable à HashTable
, HashMap
. Par défaut, la méthode hashCode()
de Object renvoie une représentation entière de l’adresse mémoire où l’objet est stocké.
La méthode hashCode()
des objets est utilisée lorsque nous les insérons dans un HashTable
, HashMap
ou HashSet
. En savoir plus sur HashTables
sur Wikipedia.org pour référence.
Pour insérer une entrée dans la structure de données de la carte, nous avons besoin à la fois de clé et de valeur. Si la clé et les valeurs sont des types de données définis par l'utilisateur, la fonction hashCode()
de la clé déterminera l'emplacement de stockage interne de l'objet. Lorsqu'il est également nécessaire de rechercher l'objet sur 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 de clé 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. La HashTable
itère ensuite cette zone (toutes les clés avec le même code de hachage) et utilise la méthode equals()
de la clé pour rechercher la bonne clé. Une fois que la bonne clé est trouvée, l'objet stocké pour cette clé est renvoyé.
Ainsi, comme nous pouvons le constater, une combinaison des méthodes hashCode()
et equals()
est utilisée lors du stockage et lors de la recherche d'objets dans une HashTable
.
REMARQUES:
Utilisez toujours les mêmes attributs d'un objet pour générer les fonctions hashCode()
et equals()
. Comme dans notre cas, nous avons utilisé l'identifiant d'employé.
equals()
doit être cohérent (si les objets ne sont pas modifiés, il doit continuer à renvoyer la même valeur).
Chaque fois que a.equals(b)
, alors a.hashCode()
doit être identique à b.hashCode()
.
Si vous en substituez un, vous devez remplacer l'autre.
http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html
Puisque hashcode retourne 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 faire? Supposons que nous avons créé un nouvel objet en transmettant une valeur 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 que la même valeur de hachage est renvoyée, JVM accédera à la même adresse mémoire à chaque fois. Si plusieurs objets sont présents pour la même valeur de hachage, la méthode equals () sera utilisée pour identifier le bon objet.
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 d'essai
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 les comparaisons d'adresses. C'est pourquoi, lorsque vous comparez deux objets dans la classe Test, la méthode equals donne false, mais lorsque nous remplaçons hashcode (), il peut comparer le contenu et obtenir un résultat correct.
Lorsque vous souhaitez stocker et récupérer votre objet personnalisé en tant que clé dans Map, vous devez toujours remplacer les valeurs equals et hashCode dans votre objet personnalisé .
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 & p2 considéreront comme un seul objet et la taille map
sera seulement 1 parce qu'ils sont égaux.