Dans le manuel Hibernate, on conseille plus que fortement de réécrire les
méthodes equals et hashCode. Je comprends très bien le but de la méthode
equals et celà ne me posera normalement pas de problème pour l'écrire
correctement. Cependant je ne comprends pas tout à fait l'utilité de la
méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour
le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Fabien Bergeret
Bernard Koninckx wrote:
Bonjour à tous,
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les méthodes equals et hashCode. Je comprends très bien le but de la méthode equals et celà ne me posera normalement pas de problème pour l'écrire correctement. Cependant je ne comprends pas tout à fait l'utilité de la méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il utiliser ensuite equals pour trouver le bon. Dans la doc java, il est donc ecrit que si l'on surcharge equals, il faut egalement surcharger hashcode de telle sorte que si deux objets sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques attributs de l'objets. On doit donc caluler le hashcode a partir des meme objets que ceux sur lesquels portent le equals. Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker. Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule.
Bernard Koninckx wrote:
Bonjour à tous,
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les
méthodes equals et hashCode. Je comprends très bien le but de la méthode
equals et celà ne me posera normalement pas de problème pour l'écrire
correctement. Cependant je ne comprends pas tout à fait l'utilité de la
méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour
le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il
utiliser ensuite equals pour trouver le bon.
Dans la doc java, il est donc ecrit que si l'on surcharge equals, il
faut egalement surcharger hashcode de telle sorte que si deux objets
sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques
attributs de l'objets. On doit donc caluler le hashcode a partir des
meme objets que ceux sur lesquels portent le equals.
Une maniere simple de realiser un hashcode, c'est de faire une somme de
tous les hashcode des attributs sur lesquels porte le equals (pour les
types de base, on peut prendre le hashcode sur wrapper associe : (new
Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le
stocker. Ce calcul devient bien entendu invalide dès modification d'un
attribut a partir duquel il est calcule.
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les méthodes equals et hashCode. Je comprends très bien le but de la méthode equals et celà ne me posera normalement pas de problème pour l'écrire correctement. Cependant je ne comprends pas tout à fait l'utilité de la méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il utiliser ensuite equals pour trouver le bon. Dans la doc java, il est donc ecrit que si l'on surcharge equals, il faut egalement surcharger hashcode de telle sorte que si deux objets sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques attributs de l'objets. On doit donc caluler le hashcode a partir des meme objets que ceux sur lesquels portent le equals. Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker. Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule.
Zazoun
La méthode hashCode sert à classer les objets dans tout ce qui ressemble à une Hashtable. Elle doit être redéfinie principalement pour être cohérente avec la méthode equals (deux objets égaux au sens de la méthode equals doivent retourner la même valeur de hashcode). La javadoc de Object.hashCode et Object.equals explique tout cela.
Pour ce qui est des best practices, le récent fil de discussion "Erreur Properties.hashCode()" (news:43186ab4$0$17240$) en parle très bien.
Mon IDE génère les méthodes equals et hashcodes suivantes (avec b pouvant être null, et pas a) : public class TestHashCode {
private Object a; private Object b; private int c;
public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false; if (!a.equals(testHashCode.a)) return false; if (b != null ? !b.equals(testHashCode.b) : testHashCode.b != null) return false;
return true; }
public int hashCode() { int result; result = a.hashCode(); result = 29 * result + (b != null ? b.hashCode() : 0); result = 29 * result + c; return result; } }
La méthode hashCode sert à classer les objets dans tout ce qui
ressemble à une Hashtable. Elle doit être redéfinie principalement
pour être cohérente avec la méthode equals (deux objets égaux au
sens de la méthode equals doivent retourner la même valeur de
hashcode). La javadoc de Object.hashCode et Object.equals explique tout
cela.
Pour ce qui est des best practices, le récent fil de discussion
"Erreur Properties.hashCode()"
(news:43186ab4$0$17240$8fcfb975@news.wanadoo.fr) en parle très bien.
Mon IDE génère les méthodes equals et hashcodes suivantes (avec b
pouvant être null, et pas a) :
public class TestHashCode {
private Object a;
private Object b;
private int c;
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false;
if (!a.equals(testHashCode.a)) return false;
if (b != null ? !b.equals(testHashCode.b) : testHashCode.b !=
null) return false;
return true;
}
public int hashCode() {
int result;
result = a.hashCode();
result = 29 * result + (b != null ? b.hashCode() : 0);
result = 29 * result + c;
return result;
}
}
La méthode hashCode sert à classer les objets dans tout ce qui ressemble à une Hashtable. Elle doit être redéfinie principalement pour être cohérente avec la méthode equals (deux objets égaux au sens de la méthode equals doivent retourner la même valeur de hashcode). La javadoc de Object.hashCode et Object.equals explique tout cela.
Pour ce qui est des best practices, le récent fil de discussion "Erreur Properties.hashCode()" (news:43186ab4$0$17240$) en parle très bien.
Mon IDE génère les méthodes equals et hashcodes suivantes (avec b pouvant être null, et pas a) : public class TestHashCode {
private Object a; private Object b; private int c;
public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false; if (!a.equals(testHashCode.a)) return false; if (b != null ? !b.equals(testHashCode.b) : testHashCode.b != null) return false;
return true; }
public int hashCode() { int result; result = a.hashCode(); result = 29 * result + (b != null ? b.hashCode() : 0); result = 29 * result + c; return result; } }
Fabien Bergeret
Fabien Bergeret wrote:
Bernard Koninckx wrote:
Bonjour à tous,
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les méthodes equals et hashCode. Je comprends très bien le but de la méthode equals et celà ne me posera normalement pas de problème pour l'écrire correctement. Cependant je ne comprends pas tout à fait l'utilité de la méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il utiliser ensuite equals pour trouver le bon. Dans la doc java, il est donc ecrit que si l'on surcharge equals, il faut egalement surcharger hashcode de telle sorte que si deux objets sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques attributs de l'objets. On doit donc caluler le hashcode a partir des meme objets que ceux sur lesquels portent le equals. Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker. Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule. Juste un complement ici :
http://mindprod.com/jgloss/hashcode.html
Fabien Bergeret wrote:
Bernard Koninckx wrote:
Bonjour à tous,
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les
méthodes equals et hashCode. Je comprends très bien le but de la méthode
equals et celà ne me posera normalement pas de problème pour l'écrire
correctement. Cependant je ne comprends pas tout à fait l'utilité de la
méthode hashCode ? Si j'ai à la réécrire quels sont les bests
practices pour
le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il
utiliser ensuite equals pour trouver le bon.
Dans la doc java, il est donc ecrit que si l'on surcharge equals, il
faut egalement surcharger hashcode de telle sorte que si deux objets
sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques
attributs de l'objets. On doit donc caluler le hashcode a partir des
meme objets que ceux sur lesquels portent le equals.
Une maniere simple de realiser un hashcode, c'est de faire une somme de
tous les hashcode des attributs sur lesquels porte le equals (pour les
types de base, on peut prendre le hashcode sur wrapper associe : (new
Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le
stocker. Ce calcul devient bien entendu invalide dès modification d'un
attribut a partir duquel il est calcule.
Juste un complement ici :
Dans le manuel Hibernate, on conseille plus que fortement de réécrire les méthodes equals et hashCode. Je comprends très bien le but de la méthode equals et celà ne me posera normalement pas de problème pour l'écrire correctement. Cependant je ne comprends pas tout à fait l'utilité de la méthode hashCode ? Si j'ai à la réécrire quels sont les bests practices pour le faire ?
Je vous remercie d'avance pour vos suggestions, remarques, ...
Bernard
Dans une map, java va utiliser le hashcode pour chercher ton objet ; si
deux objets ont le meme hashcode (ce qui est tout a fait possible), il utiliser ensuite equals pour trouver le bon. Dans la doc java, il est donc ecrit que si l'on surcharge equals, il faut egalement surcharger hashcode de telle sorte que si deux objets sont egaux (au sens de equals), ils ont le meme hashcode.
Lorsque l'on surcharge equals, on se sert generalement de quelques attributs de l'objets. On doit donc caluler le hashcode a partir des meme objets que ceux sur lesquels portent le equals. Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker. Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule. Juste un complement ici :
http://mindprod.com/jgloss/hashcode.html
News
Zazoun wrote:
public class TestHashCode {
private Object a; private Object b; private int c;
public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false; if (!a.equals(testHashCode.a)) return false; if (b != null ? !b.equals(testHashCode.b) : testHashCode.b ! > null) return false;
return true; }
public int hashCode() { int result; result = a.hashCode(); result = 29 * result + (b != null ? b.hashCode() : 0); result = 29 * result + c; return result; } }
Attention cependant aux classes dérivées de la classe 'TestHashCode' qui redéfiniraient la méthode 'equals()' !
En effet, à l'appel de la méthode 'equals()' d'une instance de 'TestHashCode', si 'o' était une instance d'une classe dérivée de 'TestHashCode', 'o instanceof TestHashCode' retournerait 'true'. Ce faisant, si 'objTest' était une instance de la classe 'TestHashCode' et que 'objTestExtend' une instance d'une classe dérivée de 'TestHashCode' qui redéfinirait 'equals()', alors 'objTest.equals(objTestExtend)' pourrait retourner 'true' quand 'objTestExtend.equals(objTest)' retournerait 'false'... Or, un objet est 'equals' à un autre, si et seulement si, ce dernier est 'equals' au premier.
Valdo.
Zazoun wrote:
public class TestHashCode {
private Object a;
private Object b;
private int c;
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false;
if (!a.equals(testHashCode.a)) return false;
if (b != null ? !b.equals(testHashCode.b) : testHashCode.b ! > null) return false;
return true;
}
public int hashCode() {
int result;
result = a.hashCode();
result = 29 * result + (b != null ? b.hashCode() : 0);
result = 29 * result + c;
return result;
}
}
Attention cependant aux classes dérivées de la classe 'TestHashCode' qui
redéfiniraient la méthode 'equals()' !
En effet, à l'appel de la méthode 'equals()' d'une instance de
'TestHashCode', si 'o' était une instance d'une classe dérivée de
'TestHashCode', 'o instanceof TestHashCode' retournerait 'true'. Ce
faisant, si 'objTest' était une instance de la classe 'TestHashCode' et
que 'objTestExtend' une instance d'une classe dérivée de 'TestHashCode'
qui redéfinirait 'equals()', alors 'objTest.equals(objTestExtend)'
pourrait retourner 'true' quand 'objTestExtend.equals(objTest)'
retournerait 'false'... Or, un objet est 'equals' à un autre, si et
seulement si, ce dernier est 'equals' au premier.
private Object a; private Object b; private int c;
public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof TestHashCode)) return false;
final TestHashCode testHashCode = (TestHashCode) o;
if (c != testHashCode.c) return false; if (!a.equals(testHashCode.a)) return false; if (b != null ? !b.equals(testHashCode.b) : testHashCode.b ! > null) return false;
return true; }
public int hashCode() { int result; result = a.hashCode(); result = 29 * result + (b != null ? b.hashCode() : 0); result = 29 * result + c; return result; } }
Attention cependant aux classes dérivées de la classe 'TestHashCode' qui redéfiniraient la méthode 'equals()' !
En effet, à l'appel de la méthode 'equals()' d'une instance de 'TestHashCode', si 'o' était une instance d'une classe dérivée de 'TestHashCode', 'o instanceof TestHashCode' retournerait 'true'. Ce faisant, si 'objTest' était une instance de la classe 'TestHashCode' et que 'objTestExtend' une instance d'une classe dérivée de 'TestHashCode' qui redéfinirait 'equals()', alors 'objTest.equals(objTestExtend)' pourrait retourner 'true' quand 'objTestExtend.equals(objTest)' retournerait 'false'... Or, un objet est 'equals' à un autre, si et seulement si, ce dernier est 'equals' au premier.
Valdo.
Zazoun
C'est vrai, je n'avais jamais remarqué ! Ce que je vois comme solutions, c'est de marquer la méthode equals "final" quand on l'implémente, ou alors d'ajouter une ligne de comparaison des classes (du style if(!o.getClass().equals(getClass())) return false;), mais il y a sans doute mieux ?
C'est vrai, je n'avais jamais remarqué !
Ce que je vois comme solutions, c'est de marquer la méthode equals
"final" quand on l'implémente, ou alors d'ajouter une ligne de
comparaison des classes (du style if(!o.getClass().equals(getClass()))
return false;), mais il y a sans doute mieux ?
C'est vrai, je n'avais jamais remarqué ! Ce que je vois comme solutions, c'est de marquer la méthode equals "final" quand on l'implémente, ou alors d'ajouter une ligne de comparaison des classes (du style if(!o.getClass().equals(getClass())) return false;), mais il y a sans doute mieux ?
Kupee
Zazoun wrote:
C'est vrai, je n'avais jamais remarqué ! Ce que je vois comme solutions, c'est de marquer la méthode equals "final" quand on l'implémente, ou alors d'ajouter une ligne de comparaison des classes (du style if(!o.getClass().equals(getClass())) return false;), mais il y a sans doute mieux ?
Tu risques d'avoir des ennuis si tu ne peux pas surcharger la méthode equals. Euh ptet comparer les champs .class plutot que le instanceof ? par contre ca sera plus lent je pense
Zazoun wrote:
C'est vrai, je n'avais jamais remarqué !
Ce que je vois comme solutions, c'est de marquer la méthode equals
"final" quand on l'implémente, ou alors d'ajouter une ligne de
comparaison des classes (du style if(!o.getClass().equals(getClass()))
return false;), mais il y a sans doute mieux ?
Tu risques d'avoir des ennuis si tu ne peux pas surcharger la méthode
equals.
Euh ptet comparer les champs .class plutot que le instanceof ? par
contre ca sera plus lent je pense
C'est vrai, je n'avais jamais remarqué ! Ce que je vois comme solutions, c'est de marquer la méthode equals "final" quand on l'implémente, ou alors d'ajouter une ligne de comparaison des classes (du style if(!o.getClass().equals(getClass())) return false;), mais il y a sans doute mieux ?
Tu risques d'avoir des ennuis si tu ne peux pas surcharger la méthode equals. Euh ptet comparer les champs .class plutot que le instanceof ? par contre ca sera plus lent je pense
Lionel
Bernard Koninckx wrote:
Si j'ai à la réécrire quels sont les bests practices pour le faire ?
Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Sur ma VM, new Integer(5).hashCode() renvoie 5. Donc sur les entiers, pas besoin de passer par le wrapper, on peut directement retourner l'entier, c'est beaucoup plus rapide.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker.
On n'est pas forcé de prendre tous les champs en compte dans le calcul du hash code. Ce qui compte, c'est que: si o1.equals(o2) alors o1.hashCode() == o2.hashCode() mais la réciproque peut très bien ne pas être vraie (o1 et o2 ayant même hashCode mais n'étant pas égaux). Ca s'appelle une "collision" de hashage et c'est acceptable à condition que ça n'ait quand même pas une proba trop forte de se produire.
Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule.
Et donc, ne jamais modifier les objets qui sont utilisés comme clés dans une hashtable! Mieux, n'utiliser que des classes immutables (comme String) pour les clés.
Note:
Juste un complement ici : http://mindprod.com/jgloss/hashcode.html
Une maniere simple de realiser un hashcode, c'est de faire une somme de
tous les hashcode des attributs sur lesquels porte le equals (pour les
types de base, on peut prendre le hashcode sur wrapper associe : (new
Integer(age)).hashcode()) par exemple.
Sur ma VM, new Integer(5).hashCode() renvoie 5. Donc sur les entiers, pas
besoin de passer par le wrapper, on peut directement retourner l'entier,
c'est beaucoup plus rapide.
Si le calcul du hashcode est complexe, il peut etre interessant de le
stocker.
On n'est pas forcé de prendre tous les champs en compte dans le calcul du
hash code.
Ce qui compte, c'est que:
si o1.equals(o2) alors o1.hashCode() == o2.hashCode()
mais la réciproque peut très bien ne pas être vraie (o1 et o2 ayant même
hashCode mais n'étant pas égaux).
Ca s'appelle une "collision" de hashage et c'est acceptable à condition que
ça n'ait quand même pas une proba trop forte de se produire.
Ce calcul devient bien entendu invalide dès modification d'un attribut a
partir duquel il est calcule.
Et donc, ne jamais modifier les objets qui sont utilisés comme clés dans
une hashtable!
Mieux, n'utiliser que des classes immutables (comme String) pour les clés.
Note:
Juste un complement ici :
http://mindprod.com/jgloss/hashcode.html
Une maniere simple de realiser un hashcode, c'est de faire une somme de tous les hashcode des attributs sur lesquels porte le equals (pour les types de base, on peut prendre le hashcode sur wrapper associe : (new Integer(age)).hashcode()) par exemple.
Sur ma VM, new Integer(5).hashCode() renvoie 5. Donc sur les entiers, pas besoin de passer par le wrapper, on peut directement retourner l'entier, c'est beaucoup plus rapide.
Si le calcul du hashcode est complexe, il peut etre interessant de le stocker.
On n'est pas forcé de prendre tous les champs en compte dans le calcul du hash code. Ce qui compte, c'est que: si o1.equals(o2) alors o1.hashCode() == o2.hashCode() mais la réciproque peut très bien ne pas être vraie (o1 et o2 ayant même hashCode mais n'étant pas égaux). Ca s'appelle une "collision" de hashage et c'est acceptable à condition que ça n'ait quand même pas une proba trop forte de se produire.
Ce calcul devient bien entendu invalide dès modification d'un attribut a partir duquel il est calcule.
Et donc, ne jamais modifier les objets qui sont utilisés comme clés dans une hashtable! Mieux, n'utiliser que des classes immutables (comme String) pour les clés.
Note:
Juste un complement ici : http://mindprod.com/jgloss/hashcode.html
madprog
Ca s'appelle une "collision" de hashage et c'est acceptable à condition que ça n'ait quand même pas une proba trop forte de se produire.
C'est entre autre pour ça que le MD5 est tellement utilisé pour identifier des fichiers : 16 octets => 2^128 possibilités => une chance sur 3,4e38 de retomber sur le même hash...
madprog
Ca s'appelle une "collision" de hashage et c'est acceptable à condition que
ça n'ait quand même pas une proba trop forte de se produire.
C'est entre autre pour ça que le MD5 est tellement utilisé pour
identifier des fichiers : 16 octets => 2^128 possibilités => une chance
sur 3,4e38 de retomber sur le même hash...
Ca s'appelle une "collision" de hashage et c'est acceptable à condition que ça n'ait quand même pas une proba trop forte de se produire.
C'est entre autre pour ça que le MD5 est tellement utilisé pour identifier des fichiers : 16 octets => 2^128 possibilités => une chance sur 3,4e38 de retomber sur le même hash...