Objects.hash()

Build a hashCode from several fields in one line, null-safe and consistent with an equals that compares the same fields.

Signature
public static int hash(Object... values)
  • Added in Java Java 7

Parameters

ParameterTypeDescription
$valuesObject...The values to hash, usually the same fields that equals compares. Any of them may be null.

Return value

intA hash code computed from all the values, the same as Arrays.hashCode(values). Null values contribute 0.

Errors

Nothing itself. Any exception thrown by a value's hashCode propagates.

Objects.hash(a, b, c) combines the hash codes of its arguments into one int. It is the standard way to write hashCode for a class whose equals compares several fields. Null arguments are fine: they count as 0.

The contract it helps you keep

If two objects are equal, they must return the same hash code. HashMap, HashSet and every cache built on them rely on this: they find the bucket by hash code first and only then call equals. Override equals without hashCode and a lookup with an equal key silently misses. The simple rule is to pass Objects.hash exactly the fields that equals compares, no more and no fewer.

@Override
public int hashCode() {
    return Objects.hash(tenantId, sku, warehouse);
}

Mistakes people make

  • Calling Objects.hash(value) for a single value and expecting value.hashCode(). It hashes a one-element array, so the result is 31 + value.hashCode(). For one value use Objects.hashCode(value), which returns 0 for null.
  • Passing an array field. The array's identity hash is used, not its contents. Use Arrays.hashCode(field) as one of the arguments.
  • Hashing mutable fields of an object that sits in a HashSet or is a map key. Change the field and the entry is stranded in the wrong bucket. Keys should be immutable; records are ideal.
  • Relying on the actual number. Hash codes are not stable identifiers, not unique, and must never be stored or sent to clients as ids.

Cost

Each call allocates a varargs array and boxes any primitive arguments. For most classes that is irrelevant. In a tight loop over millions of keys, a hand-written 31 * h + Long.hashCode(id) chain avoids the allocation and gives the same result. Records already generate a field-based hashCode without any of this, so prefer a record for value types and keep Objects.hash for ordinary classes.

Back-end angle

Composite cache keys (tenant plus resource id), de-duplication sets during an import, and entity classes that cannot be records are where you write hashCode by hand, and where a missing or mismatched one causes cache misses that are hard to trace.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Implementing hashCode for a composite cache key such as tenant plus resource id.
  • Giving an entity class that cannot be a record a hashCode consistent with its equals, so it works in sets and as a map key.
  • De-duplicating imported rows in a HashSet using a small key class built from several columns.

Frequently asked questions

What is the difference between Objects.hash and Objects.hashCode?

Objects.hashCode(o) returns o.hashCode(), or 0 for null, and takes exactly one argument. Objects.hash(values...) hashes the array of all its arguments, so even with one argument the result differs from that value's own hashCode.

Do I need to override hashCode if I override equals?

Yes. Equal objects must have equal hash codes, otherwise HashMap and HashSet lookups fail for keys that are equal but were created separately.

Is Objects.hash slow?

It allocates an array and boxes primitives on every call. That rarely matters, but in very hot code a hand-written 31 * h + ... chain is cheaper and gives the same value.

More examples

Forgetting hashCode breaks HashSet

Without hashCode the two equal Sku objects land in different buckets, so a set can hold duplicates and contains misses (the exact count depends on identity hashes, so it is not printed). Adding a hashCode over the same field fixes both; with a single field, Objects.hashCode is the right call.

Example · java
final class Sku {
    final String code;
    Sku(String code) { this.code = code; }

    @Override
    public boolean equals(Object o) {
        return o instanceof Sku s && Objects.equals(code, s.code);
    }
    // no hashCode override
}

final class FixedSku {
    final String code;
    FixedSku(String code) { this.code = code; }

    @Override
    public boolean equals(Object o) {
        return o instanceof FixedSku s && Objects.equals(code, s.code);
    }

    @Override
    public int hashCode() { return Objects.hashCode(code); }
}

void main() {
    var broken = new HashSet<Sku>();
    broken.add(new Sku("AB-1"));
    broken.add(new Sku("AB-1"));

    var fixed = new HashSet<FixedSku>();
    fixed.add(new FixedSku("AB-1"));
    fixed.add(new FixedSku("AB-1"));

    IO.println(new Sku("AB-1").equals(new Sku("AB-1")));
    IO.println(fixed.size() + " " + fixed.contains(new FixedSku("AB-1")));
}
// Output:
// true
// 1 true

Null fields and argument order

A null field contributes 0 instead of throwing, and the order of arguments changes the result. Objects.hash is defined as Arrays.hashCode of its arguments.

Example · java
void main() {
    IO.println(Objects.hash("orders", null));
    IO.println(Objects.hash("orders", null) == Objects.hash("orders", null));
    IO.println(Objects.hash("a", "b") == Objects.hash("b", "a"));
    IO.println(Objects.hash("a", "b") == Arrays.hashCode(new Object[] {"a", "b"}));
}
// Output:
// -1207108228
// true
// false
// true

Hand-written hash without boxing

The manual version follows the same 31 * h formula, so it produces identical values without allocating an array or boxing the long. Use it only where profiling shows hashCode is hot.

Example · java
int viaObjectsHash(long id, String region) {
    return Objects.hash(id, region);
}

int handWritten(long id, String region) {
    int h = 1;
    h = 31 * h + Long.hashCode(id);
    h = 31 * h + Objects.hashCode(region);
    return h;
}

void main() {
    IO.println(viaObjectsHash(1001L, "eu-west") == handWritten(1001L, "eu-west"));
    IO.println(viaObjectsHash(7L, null) == handWritten(7L, null));
}
// Output:
// true
// true

Discussion

  • Be the first to comment on this lesson.