Objects.hash() vs Objects.hashCode(), clarification needed

后端 未结 3 1236
孤城傲影
孤城傲影 2020-12-04 14:29

in Java 7 we have

o.hashCode();
Objects.hashCode(o);

    Objects.hash(o);

The first 2 are roughly the same with the null point check, but

3条回答
  •  独厮守ぢ
    2020-12-04 14:47

    Objects.hashCode

    The utility method Objects.hashCode( Object o ) simply calls the hashCode method on the passed object.

    Tolerates NULL

    So why invent or use this method? Why not just call the object’s hashCode method yourself?

    This method offers one benefit: NULL0. The utility method tolerates a null.

    • If you call Objects.hashCode( myObject ) where myObject is NULL, you get back a zero (0).
    • In contrast, calling myObject.hashCode() when myObject is NULL throws a NullPointerException argument.

    Whether tolerating a null is desirable or not depends on your own judgement in your particular situation.

    Objects.hash

    The utility method Objects.hash( Object o , … ) serves a different purpose. This method has two phases:

    • Call .hashCode on each passed object, collecting each result.
    • Calculate a hash on the collected results.

    hash of a hash

    If you pass a single object, Objects.hash( myObject ), first myObject.hashCode is called and collected, and then a hash on that single-item collection is calculated. So, you end up with a hash of a hash.

    When hashing a single object, it is vital to understand that Objects.hashCode( myObject ) returns a different result than Objects.hash( myObject ). Effectively, the second returns a hash on the result of the first.

    Annoying in practice

    The logic of the approach taken in these two Objects methods makes sense, in and of themselves.

    Unfortunately, in practice, for those of us trying to use them in day-to-day use when writing code on our POJOs to override hashCode, and correspondingly equals, we must think twice to decide which to call.

    • If your hashCode (and equals) override is based on a single member of your class, use Objects.hashCode( member ).
    • If your hashCode (and equals) override is based on multiple attribute of your class, use Objects.hash( memberA , memberB , memberC ).

    Single member, not tolerating a NULL

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

    Single member, tolerating a NULL

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

    Multi-member, tolerating a 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.  
    }
    

    Example

    We can test these various methods quite simply.

    UUID

    Let's use a UUID object as an example. A UUID (universally unique identifier) is a 128-bit value where certain bits have certain semantics.

    The OpenJDK implementation of UUID internally represents the 128-bit value as a pair of 64-bit long integer numbers.

    That same implementation overrides Object::equals and Object::hashCode to look at the data stored in that pair of long integers. Here is the source code for those two methods.

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

    Example code

    Instantiate our UUID object.

    UUID uuid = UUID.randomUUID();
    

    Calculate our hash values.

    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 to console.

    System.out.println( "uuid.toString(): " + uuid.toString() );
    System.out.println( " 1/2 = " + hash1 + " | " + hash2 );
    System.out.println( " 3/4 = " + hash3 + " | " + hash4 );
    

    See this code run live at IdeOne.com.

    uuid.toString(): 401d88ff-c75d-4607-bb89-1f7a2c6963e1

    1/2 = 278966883 | 278966883

    3/4 = 278966914 | 278966914

提交回复
热议问题