dans Java 7 nous avons
o.hashCode();
Objects.hashCode(o);
Objects.hash(o);
Les 2 premiers sont à peu près les mêmes avec la vérification du point nul, mais quel est le dernier?
Lorsqu'une seule référence d'objet est fournie, la valeur renvoyée n'est pas égale au code de hachage de cette référence d'objet.
Pourquoi donc? Je veux dire, nous n'avons pas besoin de 3 méthodes qui font la même chose, je le comprends, mais pourquoi avons-nous besoin de Objects.hash()
du tout? Quand choisiriez-vous d'utiliser l'un contre l'autre?
Consultez la documentation de hashCode
et hash
. hash
prend Object...
tandis que hashCode
prend Object
. L'exemple donné est:
@Override public int hashCode() {
return Objects.hash(x, y, z);
}
Objects.hash(Object... values)
doit être utilisé dans les cas où vous voulez un hachage d'une séquence d'objets, par ex. lorsque vous définissez votre propre méthode hashCode
et que vous souhaitez un hachage simplement codé pour plusieurs valeurs qui composent l'identité de votre objet.Objects.hashCode(Object o)
doit être utilisé lorsque vous voulez le hachage d'un seul objet, sans lancer si l'objet est nul.Object::hashCode()
doit être utilisé lorsque vous voulez le hachage d'un seul objet, et lèvera une exception si l'objet est nul.Notez que hash(o)
et hashCode(o)
ne retourneront pas nécessairement la même chose! Si vous le faites pour un seul objet, vous devriez probablement utiliser hashCode
.
Objects.hashCode
La méthode utilitaire Objects.hashCode( Object o )
appelle simplement la méthode hashCode
sur l'objet passé.
Alors pourquoi inventer ou utiliser cette méthode? Pourquoi ne pas simplement appeler vous-même la méthode hashCode
de l’objet?
Cette méthode offre un avantage: NULL
➙ 0
. La méthode utilitaire tolère un null.
Objects.hashCode( myObject )
où myObject
est NULL
, vous obtenez un zéro (0).myObject.hashCode()
lorsque myObject
est NULL
lance un argument NullPointerException
.Qu'il soit souhaitable ou non de tolérer une valeur nulle dépend de votre propre jugement dans votre situation particulière.
Objects.hash
La méthode utilitaire Objects.hash( Object o , … )
sert un objectif différent. Cette méthode comporte deux phases:
.hashCode
Sur chaque objet passé, en collectant chaque résultat.Si vous passez un seul objet, Objects.hash( myObject )
, d'abord myObject.hashCode
Est appelé et collecté, puis un hachage sur cette collection d'élément unique est calculé. Donc, vous vous retrouvez avec un hachage d'un hachage.
Lors du hachage d'un seul objet, il est essentiel de comprendre que Objects.hashCode( myObject )
renvoie un résultat différent de Objects.hash( myObject )
. En effet, le second retourne un hachage sur le résultat du premier.
La logique de l'approche adoptée dans ces deux méthodes Objects
est logique en soi.
Malheureusement, dans la pratique, pour ceux d'entre nous qui tentent de les utiliser au quotidien lors de l'écriture de code sur nos POJO pour remplacer hashCode
, et en conséquence equals
, nous devons réfléchir à deux fois pour décider qui appeler.
hashCode
(et equals
) est basée sur un seul membre de votre classe, utilisez Objects.hashCode( member )
.hashCode
(et equals
) est basée sur plusieurs attributs de votre classe, utilisez Objects.hash( memberA , memberB , memberC )
.@Override
public int hashCode() {
return this.member.hashCode() ; // Throws NullPointerException if member variable is null.
}
@Override
public int hashCode() {
return Objects.hashCode( this.member ) ; // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}
@Override
public int hashCode() {
return Objects.hash( this.memberA , this.memberB , this.memberC ) ; // Hashes the result of all the passed objects’ individual hash codes.
}
Nous pouvons tester ces différentes méthodes tout simplement.
Prenons un objet UUID
comme exemple. Un UUID ( identifiant universellement unique ) est une valeur de 128 bits où certains les bits ont une certaine sémantique.
L'implémentation OpenJDK de UUID
représente en interne la valeur 128 bits sous la forme d'une paire de nombres entiers 64 bits long
.
Cette même implémentation remplace Object::equals
Et Object::hashCode
Pour regarder les données stockées dans cette paire de longs entiers. Voici le code source de ces deux méthodes .
public boolean equals(Object obj) {
if ((null == obj) || (obj.getClass() != UUID.class))
return false;
UUID id = (UUID)obj;
return (mostSigBits == id.mostSigBits &&
leastSigBits == id.leastSigBits);
}
public int hashCode() {
long hilo = mostSigBits ^ leastSigBits;
return ((int)(hilo >> 32)) ^ (int) hilo;
}
Instanciez notre objet UUID.
UUID uuid = UUID.randomUUID();
Calculez nos valeurs de hachage.
int hash1 = uuid.hashCode();
int hash2 = Objects.hashCode( uuid ); // Result matches line above.
int hash3 = Objects.hash( uuid ); // Returns a hash of a hash.
int hash4 = Objects.hash( uuid.hashCode() ); // Result matches line above.
Dump sur console.
System.out.println( "uuid.toString(): " + uuid.toString() );
System.out.println( " 1/2 = " + hash1 + " | " + hash2 );
System.out.println( " 3/4 = " + hash3 + " | " + hash4 );
Voir ceci code exécuté en direct sur IdeOne.com .
uuid.toString (): 401d88ff-c75d-4607-bb89-1f7a2c6963e1
1/2 = 278966883 | 278966883
3/4 = 278966914 | 278966914
Le hashCode () par défaut de Object renvoie l'adresse mémoire de l'objet. Donc, si vous avez la classe suivante:
class Car {
String make;
String model;
int year;
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
}
puis créez deux objets:
Car car1 = new Car("Toyota", "Corolla", 2010);
Car car2 = new Car("Toyota", "Corolla", 2010);
car1.hashCode () sera différent de car2.hashCode () car chaque objet aura une adresse mémoire différente.
Que faire si vous voulez que car1 et car2 renvoient le même code de hachage? Dans ce cas, vous devez remplacer la méthode Object hashCode () par défaut pour la classe Car comme suit:
@Override
public int hashCode() {
Object[] x = {model, make, Integer.valueOf(year)};
int hashArray = Arrays.hashCode(x);
return hashArray;
}
Cela rendra car1.hashCode () égal à car2.hashCode () car String.hashCode () calcule le hashCode en fonction du contenu de la chaîne et Integer.hashCode () renverra lui-même la valeur entière.
Dans Java 7, vous pouvez simplement utiliser Objects.hash (valeurs Object ...). Ainsi, notre nouveau Car hashCode () ressemblera à ceci:
@Override
public int hashCode() {
return Objects.hash(model, make, year);
}
Objects.hash (Object ... values) appellera Arrays.hashCode pour vous.
Enfin, Objects.hashCode (Object o) fera une vérification nulle. Il renverra 0 si l'objet est nul. Sinon, il appellera la méthode objets hashCode ().