Use Objects.hash() or own hashCode() implementation?

JavaHash

Java Problem Overview


I have recently discovered the Objects.hash() method.

My first thought was, that this tidies up your hashCode() implementation a lot. See the following example:

@Override
//traditional
public int hashCode() {
	int hash = 5;
	hash = 67 * hash + (int)(this.id ^ (this.id >>> 32));
	hash = 67 * hash + (int)(this.timestamp ^ (this.timestamp >>> 32));
	hash = 67 * hash + Objects.hashCode(this.severity);
	hash = 67 * hash + Objects.hashCode(this.thread);
	hash = 67 * hash + Objects.hashCode(this.classPath);
	hash = 67 * hash + Objects.hashCode(this.message);
	return hash;
}

@Override
//lazy
public int hashCode() {
	return Objects.hash(id, timestamp, severity, thread, classPath, message);
}

Although I have to say that this seems too good to be true. Also I've never seen this usage.

Are there any downsides of using Objects.hash() compared to implementing your own hash code? When would I choose each of those approaches?

Update

Although this topic is marked as resolved, feel free to keep posting answers that provide new information and concerns.

Java Solutions


Solution 1 - Java

Note that the parameter of Objects.hash is Object.... This has two main consequences:

  • Primitive values used in the hash code calculation have to be boxed, e.g. this.id is converted from long to Long.
  • An Object[] has to be created to invoke the method.

The cost of creating of these "unnecessary" objects may add up if hashCode is called frequently.

Solution 2 - Java

Following is implementation of Objects.hash - which is calling Arrays.hashCode internally.

public static int hash(Object... values) {
    return Arrays.hashCode(values);
}

This is implementation of Arrays.hashCode method

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

So I agree with @Andy The cost of creating of these "unnecessary" objects may add up if hashCode is called frequently. If you are implementing yourself it would be faster.

Solution 3 - Java

I would like to try and make a strong argument for both.

Opening disclaimer
For this answer, Objects.hash(), Objects.hashCode(), and any function provided by any library that performs this role are interchangeable. First, I would like to argue, use Objects.hash() or don't use the static Objects functions at all. Any argument for or against this method requires making assumptions about the compiled code that are not guaranteed to be true. (For example, the compiler optimizer may convert the function call into an inline call, thus bypassing the extra call stack and object allocation. Just like how loops that do nothing useful don't make it to the compiled version (unless you turn off the optimizer). You also have no guarantee that future Java versions won't include the JVM version like C# does in it's version of this method. (for security reasons I believe)) So the only safe argument you can make regarding using this function, is that it is generally safer to leave the details of a proper hash to this function than to try to implement your own naive version.

For Objects.hash

  • Guaranteed to be a good hash.
  • Takes 5 seconds to implement.
  • Yours would have had a bug in it (somehow especially if you copy-pasted the implementation)

Against Objects.hash

  • The Java docs make no promises about hash cross-compatibility (Will a JVM v6 and JVM v8 give the same values? always? across OS?)

  • The thing about hashCodes, They work best if "evenly distributed". So if an int value is only valid for range 1 to 100, you might want to "redistribute" its hash-codes to not all be part of the same bucket.

  • If you have any requirement that makes you question how Objects.hash works, reliability/performance wise, Think carefully if the hash-code is really what you want, and implement a custom hash-coding method that addresses your needs.

Solution 4 - Java

Joshua Bloch in his book Effective Java, 3rd edition, p. 53 discourages usage of Objects.hash(...) if performance is critical.

Primitives are being autoboxed and there is a penalty of creating an Object array.

Solution 5 - Java

Personally I'd side with the short code first, because it is so much quicker to read, change and verify as correct, which all serve to avoid bugs when modifying the class.

Then for performance-critical classes, or where fields are costly to hash, another available "tool" is to cache the result (like String does):

// volatile not required for 32-bit sized primitives
private int hash;

@Override
public final int hashCode() {
    // "Racy Single-check idiom" (Item 71, Effective Java 2nd ed.)
    int h = hash;
    if (h == 0) {
        h = Objects.hash(id, timestamp, severity, thread, classPath, message);
        hash = h;
    }
    return h;
}

In this lockless-threadsafe pattern (which assumes an immutable class, naturally) there's a slim chance that hash might get initialised by different threads multiple times, but this does not matter because the result of the public method is always identical. The key to (memory-visibility) correctness is ensuring that hash is never written and read more than once in the method.

The array-construction penalty of Objects.hash might be lower than you imagine once the code gets JIT-inlined & optimised by the C2 compiler.

(UPDATED) The good folks over at JDK had something baking that was pitched at eliminating the overheads of Objects::hash mentioned in other answers, but apparently the difference was not enough to matter, to that particular method: JEP 348: Java Compiler Intrinsics for JDK APIs

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHerr DerbView Question on Stackoverflow
Solution 1 - JavaAndy TurnerView Answer on Stackoverflow
Solution 2 - Javanagendra547View Answer on Stackoverflow
Solution 3 - JavaTezraView Answer on Stackoverflow
Solution 4 - JavaIhor M.View Answer on Stackoverflow
Solution 5 - JavaLuke UsherwoodView Answer on Stackoverflow