web-dev-qa-db-fra.com

Objects.hash () vs Objects.hashCode (), clarification nécessaire

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?

51
JAM

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.

73
Tim S.

Objects.hashCode

La méthode utilitaire Objects.hashCode( Object o ) appelle simplement la méthode hashCode sur l'objet passé.

Tolère NULL

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: NULL0. La méthode utilitaire tolère un null.

  • Si vous appelez Objects.hashCode( myObject )myObject est NULL, vous obtenez un zéro (0).
  • En revanche, appeler 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:

  • Appelez .hashCode Sur chaque objet passé, en collectant chaque résultat.
  • Calculez un hachage sur les résultats collectés.

hachage d'un hachage

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.

Ennuyeux dans la pratique

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.

  • Si votre substitution hashCode (et equals) est basée sur un seul membre de votre classe, utilisez Objects.hashCode( member ).
  • Si votre substitution hashCode (et equals) est basée sur plusieurs attributs de votre classe, utilisez Objects.hash( memberA , memberB , memberC ).

Membre unique, ne tolérant pas un NULL

@Override
public int hashCode() {
    return this.member.hashCode() ;  // Throws NullPointerException if member variable is null.
}

Membre unique, tolérant un NULL

@Override
public int hashCode() {
    return Objects.hashCode( this.member ) ;  // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}

Multi-membre, tolérant un NULL

@Override
public int hashCode() {
    return Objects.hash( this.memberA , this.memberB , this.memberC  ) ;  // Hashes the result of all the passed objects’ individual hash codes.  
}

Exemple

Nous pouvons tester ces différentes méthodes tout simplement.

UUID

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;
}

Exemple de code

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

13
Basil Bourque

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 ().

11
ezzadeen