Académique Documents
Professionnel Documents
Culture Documents
Symtrie : pour deux rfrences a et b, si a.equals(b) alors il faut obligatoirement que b.equals(a)
Rflexivit : pour toute rfrence non null, a.equals(a) doit toujours renvoyer true
Consistance avec la mthode hashCode() : si deux objets sont gaux en invoquant la mthode
equals() alors leur mthode hashCode() doit renvoyer la mme valeur pour les deux objets
Pour toute rfrence non null, a.equals(null) doit toujours renvoyer false
Aucune spcification n'est impose concernant l'implmentation des mthodes equals() et hashCode() pour
leur permettre d'tre consistantes.
Cependant, l'implmentation de la mthode hashcode() doit tre consistante avec la mthode equals() : si la
mthode equals() renvoie true pour deux objets alors la mthode hashCode() invoque sur les deux objets
doit renvoyer la mme valeur. L'inverse n'est pas vrai, deux objets dont la mthode hashCode() renvoie la
mme valeur, n'implique pas obligatoirement que l'invocation de la mthode equals() sur les deux objets
renvoie true.
Il est donc ncessaire de redfinir les mthodes hashCode() et equals() de manire coordonne si l'une ou
l'autre est redfinie. Pour garantir le contrat entre les mthodes equals() et hashCode() et leur efficacit
maximale, il est prfrable que leur implmentation utilise les mmes champs de la classe.
Pour les classes qui implmentent l'interface Comparable, il est aussi important de maintenir une cohrence
entre les mthodes equals() / hashCode() et la mthode compareTo(). Les spcifications prcisent que si la
mthode compareTo() renvoie 0 alors la mthode equals() doit renvoyer true et inversement. Cela implique
aussi que si equals() renvoie false, alors la mthode compareTo() doit renvoyer une valeur diffrente de 0.
Rsultat :
1.false
2.true
Deux mmes objets sont gaux s'ils possdent la mme rfrence videment mais deux objets distincts
peuvent aussi tre gaux si l'invocation de la mthode equals() du premier avec le second en paramtre
renvoie true.
L'implmentation par dfaut de la mthode equals() dans la classe Object est la suivante :
Exemple :
Par dfaut, l'implmentation de la mthode equals() hrite de la classe Object teste donc l'galit de
l'adresse mmoire des objets.
Il y a un contrat respecter entre les mthodes equals() et hashCode() : comme prcis dans la javadoc, si
l'invocation de la mthode equals() avec deux instances renvoie true alors l'invocation de la mthode
hashCode() de ces deux instances doit renvoyer la mme valeur. Cette implmentation respecte ce contrat.
Cette implmentation par dfaut de la mthode equals(), hrite de la classe Object, a le mrite de
fonctionner pour tous les objets mais son mode de fonctionnement n'est pas toujours souhait pour tous les
objets.
Exemple :
01.import java.util.Date;
02.
03.public class Personne {
04.
05.private String nom;
06.private String prenom;
07.private long id;
08.private Date dateNaiss;
09.private boolean adulte;
10.
11.public Personne(String nom, String prenom, long id, Date dateNaiss,
12.boolean adulte) {
13.super();
14.this.nom = nom;
15.this.prenom = prenom;
16.this.id = id;
17.this.dateNaiss = dateNaiss;
18.this.adulte = adulte;
19.}
20.}
Si l'on cre deux instances de cette classe avec les mme paramtres et que l'on teste l'galit sur les
deux instances, le rsultat est false puisque ce sont deux instances distinctes.
Exemple :
Rsultat :
1.false
Logiquement, on pourrait esprer que ce test renvoie true mais pour cela il faut redfinir la mthode
equals().
deux objets de type String sont gaux si les deux chanes possdent la mme squence de
caractres
deux objets de type Integer sont gaux si leur valeur est gale
Il est prfrable d'utiliser les champs qui concernent l'tat de l'objet : ceci implique gnralement de ne
pas prendre en compte les champs static et les champs transient.
L'implmentation de la mthode equals() n'est pas toujours facile et dpend de la classe. Si la classe est
immuable alors l'implmentation de la mthode equals() peut utiliser la comparaison de l'tat de l'objet avec
l'tat de l'objet fourni en paramtre.
L'implmentation de la mthode equals() pour une classe qui n'est pas immuable est plus difficile car il faut
dcider si l'galit va se faire sur tout ou partie de l'tat de l'objet ou sur l'identit de l'objet
(l'implmentation de la classe Object utilise la rfrence par exemple). Ce choix dpend de l'utilisation qui
sera faite des instances de la classe.
L'implmentation de la mthode equals() peut parfois tre complexe selon les besoins. Par exemple, la
mthode equals() de l'interface List vrifie que l'autre objet est aussi de type List, que les deux collections
possdent le mme nombre d'lments, qu'ils contiennent les mmes lments en utilisant leur mthode
equals() et que ces lments sont dans le mme ordre.
tre rflexive : pour tout objet x, x.equals(x) doit retourner true. Un objet doit tre gal luimme
tre symtrique : pour tout objet x et y, si x.equals(y) renvoie true alors y.equals(x) doit renvoyer
true. Si un objet est gal un autre alors l'autre doit tre gal l'objet
tre transitive : pour tout objet x,y et z, si x.equals(y) renvoie true et y.equals(z) renvoie true
alors x.equals(z) doit renvoyer true. Si un premier objet est gal un second et que le second est
gal un troisime alors le premier doit tre gal au troisime
tre cohrent : pour tout objet x et y gaux, plusieurs invocations de la mthode x.equals(y), sans
modification de x ou y, renvoient de faon consistante la mme valeur
ne jamais tre gal null : pour tout objet x non null, x.equals(null) doit toujours renvoyer false
pour respecter les spcifications, l'implmentation de la mthode equals() doit tre en relation
avec celle de la mthode hashCode() pour garantir que deux objets gaux renvoient le mme hash
code. Cependant, l'inverse n'est pas obligatoirement vrai
Le non respect des rgles qui dfinissent le contrat de la mthode equals() peut induire des bugs difficiles
identifier car ce sont des problmes de conception.
Il n'existe pas de solution unique pour redfinir la mthode equals() tant que les contraintes imposes par la
spcification sont respectes. La plupart des IDE propose mme une fonctionnalit pour gnrer cette
mthode partir de tout ou partie des champs de la classe.
Exemple :
01.import java.util.Date;
02.
03.public class Personne {
04.
05.private String nom;
06.private String prenom;
Lors de la redfinition de la mthode equals(), il faut bien faire attention respecter la signature de la
mthode qui attend en paramtre une instance de type Object sinon c'est une surcharge qui se compilera
sans soucis mais qui ne sera pas invoque pour tester l'galit.
Pour viter ce problme, il faut utiliser l'annotation @Override sur la redfinition de la mthode, assurant
ainsi une erreur la compilation si la mthode n'est pas une redfinition d'une mthode hrite.
La redfinition de la mthode equals() n'est pas obligatoire et n'est pas toujours forcement ncessaire
notamment :
si chaque instance de la classe est unique ou s'il n'existe qu'une seule instance de la classe
(singleton)
Si le test de l'galit d'une classe n'a pas de sens, il est prfrable de redfinir la mthode equals() pour
qu'elle lve une exception de type UnsupportedOperationException. Ceci permet d'viter d'avoir le
comportement d'une des classes mre, qui peut tre celui de la classe Object.
La mthode equals() est frquemment utilise notamment dans la plupart des implmentations de collections
pour savoir si un objet est dj prsent dans la collection ou non. Il est donc important que la redfinition
de la mthode equals() soit optimise et efficace surtout si le nombre d'instances dans la collection est
important.
Pour optimiser ces performances, il est par exemple possible de suivre quelques recommandations :
il faut rapidement tester si l'instance passe en paramtre est null pour renvoyer directement
false
comme l'instance fournie en paramtre est de type Object, il faut tester si la classe de l'instance
courante est identique la classe de l'objet fourni en paramtre de la mthode ou si celui-ci est
une instance de la classe courante : si ce n'est pas le cas, il faut renvoyer directement false sinon il
est possible de caster le paramtre dans le type de la classe courante pour permettre des tests
sur les valeurs des champs
il peut tre intressant de faire les comparaisons des valeurs des champs les plus rapides en
premier comme par les champs de type int. Si la valeur est diffrente, il n'est pas ncessaire de
tester les valeurs des autres champs
Par contrat, la mthode equals() attend un objet de type Object : il est donc prfrable avant de tester
l'galit des membres de la classe de s'assurer de l'galit du type de la classe avec celui de celle fournie
en paramtre.
Il y a deux manires de vrifier l'galit de la classe avant de vrifier l'galit des membres :
utiliser la mthode equals() sur les classes des deux objets obtenues en invoquant leur mthode
getClass()
Chacune de ces solutions a son utilit selon les circonstances et l'utilisation de l'une ou l'autre dpend des
besoins.
Il est gnralement prfrable de tester que les objets soient du mme type en testant l'galit de
l'invocation de leur mthode getClass(). Ce test permet de renvoyer false si l'instance fournie en paramtre
est une sous-classe de l'instance courante. Ce type de test n'est pas obligatoire mais dans ce cas, les
classes qui peuvent tre passes en paramtre de la mthode equals() doivent faire de mme pour respecter
la rgle de symtrie et de rflexivit.
Cependant, pour certains cas particuliers, il peut tre souhaitable de tester que les objets soient du mme
type en utilisant l'oprateur instanceof. Un exemple de cas particulier concerne les entits utilises avec
Hibernate : comme ce dernier peut crer des proxys, il est prfrable d'utiliser l'oprateur instanceof.
Attention cependant, ce n'est gnralement pas une bonne ide d'utiliser l'oprateur instanceof lorsque la
mthode equals() doit tre redfinie car gnralement cela peut violer la rgle de symtrie que doit
respecter l'implmentation de la mthode equals().
Le test sur l'galit des classes des deux instances permet de pouvoir tendre la classe sans avoir
redfinir la mthode equals() pour respecter la rgle concernant la symtrie.
Cette mthode est dclare native car c'est l'implmentation de la JVM qui peut obtenir l'adresse mmoire
de l'objet. Par dfaut, la mthode hashCode(), dfinie dans la classe Object, utilise l'adresse mmoire de
l'instance pour crer la valeur de type int du hashcode de l'instance.
Il est cependant possible de redfinir cette mthode puisque toutes les classes hritent de la classe
Object.
Rsultat :
1.chaine.hashCode() = -921457200
2.chaine identityHashcode = 4072869
3.monObjet.hashCode() = 1671711
4.monObjet identityHashcode = 1671711
combine ces valeurs en utilisant un multiplicateur (gnralement un nombre premier) pour dterminer la
valeur de hachage.
Il n'existe pas de solution unique pour redfinir la mthode hashCode() tant que les contraintes imposes
par la spcification sont respectes.
Exemple :
01.import java.util.Date;
02.
03.public class Personne {
04.
05.private String nom;
06.private String prenom;
07.private long id;
08.private Date dateNaiss;
09.private boolean adulte;
10.
11.@Override
12.public int hashCode() {
13.final int prime = 31;
14.int result = 1;
15.result = prime * result
16.result = prime * result
dateNaiss.hashCode());
17.result = prime * result
18.result = prime * result
19.result = prime * result
20.return result;
21.}
22.}
la valeur renvoye doit tre constante lors de plusieurs invocations sur un mme objet durant la
dure de vie de l'application. Cette valeur n'a pas d'obligation d'tre constante sur plusieurs
excutions de l'application
deux objets gaux (l'invocation de la mthode equals() sur une instance avec l'autre en paramtre
renvoie true) doivent obligatoirement avoir le mme hash code
si deux objets ne sont pas gaux en invoquant la mthode equals(), alors l'invocation de la mthode
hashCode() de chacun des objets n'a pas l'obligation de renvoyer des valeurs entires diffrentes
plus la dispersion des valeurs de hachage calcules est importante, meilleures seront les
performances lorsque l'objet sera utilis comme dans une collection de type HashXXX
L'implmentation par dfaut de la mthode hashCode(), hrite de la classe Object et qui utilise la
rfrence de l'objet, respecte ces rgles :
par dfaut, la mthode equals() vrifie l'galit des rfrences des deux objets : donc deux objets
gaux renverront la mme valeur de hachage
Deux objets gaux doivent avoir la mme valeur de hachage tant qu'ils restent gaux mais deux objets non
gaux n'ont pas l'obligation d'avoir des valeurs de hachage distinctes. Pour respecter ces deux rgles, il est
ncessaire de redfinir la mthode hashCode() lorsque la mthode equals() est redfinie.
De plus, par dfaut, la mthode hashCode() renvoie la valeur de type int dtermine partir de l'adresse
mmoire de l'instance. Cela permet d'avoir une bonne rpartition des valeurs retournes par la mthode
hashCode() mais ne permet pas de retourner la mme valeur pour deux instances dont la mthode equals()
est redfinie pour tester l'galit des valeurs de leur proprit. Il faut donc redfinir la mthode
hashCode() en consquence si la mthode equals() est redfinie et assurer ainsi une cohrence entre leurs
implmentations.
La faon la plus simple de garantir que deux objets gaux possdent la mme valeur de hachage est
d'utiliser les mmes attributs de la classe dans l'implmentation des mthodes equals() et hashCode().
La redfinition de la mthode hashCode() doit viter au maximum de renvoyer la mme valeur pour deux
instances mme si cela est quasi impossible puisque les valeurs possibles sont celles du type int du hashcode
et qu'elles doivent tre calcules le plus rapidement possible.
Le simple fait que l'implmentation de la mthode hashCode() renvoie une valeur fixe pour toutes les
instances est une implmentation qui respecte les rgles : deux objets gaux auront forcment le mme
hashcode et la valeur du hashcode d'un objet sera obligatoirement consistante lors de plusieurs invocations
de la mthode hashCode(). Cependant, cette implmentation implique de trs mauvaises performances lors
de l'utilisation dans des collections de type HashXXX.
La valeur de hachage des diffrents objets doit tre assez significative et reprsentative dans la plage des
valeurs permises par un entier de type int. Pour atteindre cet objectif, quelques rgles peuvent tre
utilises :
utiliser une formule mathmatique ddie pour chaque type primitif pour dterminer une valeur
entire : deux nombres premiers pour les boolens, dcalage de bits pour les types plus grands
qu'un entier, ...
faire des combinaisons en ajoutant la valeur de chaque attribut multiplie par un nombre premier
Une implmentation de la mthode hashCode() utilise donc frquemment un ou deux nombres premiers et
une expression mathmatique dans l'algorithme de calcul. Gnralement, l'algorithme utilise une
combinaison des valeurs de hachage des diffrents attributs qui composent la classe.
Il n'est pas forcement ncessaire d'utiliser tous les attributs mais il faut dans ce cas slectionner les
attributs qui permettront d'tre le plus discriminant. Il faut cependant garantir le respect de la rgle de
cohrence entre la mthode hashCode() et equals().
Les spcifications n'imposent aucun algorithme pour l'implmentation de la mthode hashCode() de la classe
Object. Il n'est donc pas possible de se baser sur la valeur de hachage par dfaut entre deux JVM de deux
fournisseurs.
Il est trs important d'optimiser le calcul de la valeur retourne par la mthode hashCode().
Ainsi si le calcul de la valeur du hashcode est complexe ou pour amliorer les performances, il est possible
de mettre en cache la valeur de hachage calcule. Deux cas de figure sont prendre en compte :
l'objet est immuable : dans ce cas, c'est trs facile car le hashcode peut tre calcul une seule et
unique fois lorsque l'objet est initialis
l'objet n'est pas immuable : il est alors ncessaire de recalculer la valeur du hashcode stock
chaque modification de la valeur d'un attribut. Il faut cependant dans ce cas avoir la maitrise de
tous les cas o la valeur d'un attribut peut tre modifie afin d'tre en mesure de recalculer la
nouvelle valeur de hachage
Le stockage de la valeur de hachage est donc une solution utilisable sans soucis pour un objet immuable.
Exemple :
01.import java.util.Date;
02.
03.public class PersonneImmuable {
04.private final String nom;
05.private final String prenom;
06.private final long id;
07.private final Date dateNaiss;
08.private final boolean adulte;
09.private final int cacheHashCode;
10.
11.public PersonneImmuable(String nom, String prenom, long id, Date
dateNaiss,
12.boolean adulte) {
13.super();
14.this.nom = nom;
15.this.prenom = prenom;
16.this.id = id;
17.this.dateNaiss = dateNaiss;
18.this.adulte = adulte;
19.this.cacheHashCode = calculerHashCode();
20.}
21.
22.@Override
23.public int hashCode() {
24.return cacheHashCode;
25.}
26.
27.private int calculerHashCode() {
28.final int prime = 31;
29.int result = 1;
30.result = prime * result + (adulte ? 1231 : 1237);
31.result = prime * result + ((dateNaiss == null) ? 0 :
dateNaiss.hashCode());
La valeur de hachage mise en cache peut tre utilise pour optimiser l'algorithme de la mthode equals() : si
la valeur de hachage est diffrente alors les objets ne sont pas gaux. Attention cependant, l'inverse n'est
pas vrai : si les valeurs de hachage sont gales alors les objets ne sont peut tre pas gaux.
Pour une classe qui n'est pas immuable, la mise en cache de la valeur de hash est beaucoup moins triviale car
la valeur doit tre recalcule chaque fois que la valeur d'un attribut qui entre dans le calcul de la valeur de
hachage est modifie.
La mise en cache de la valeur de hachage n'est peut tre pas une bonne ide si le nombre d'instances est
trs important car cela risque d'occuper beaucoup de place dans le heap.