# Understanding Guava's Hashing Utilities: HashFunction, Hasher, and HashCode Explained

> Master Guava's hashing framework. Learn how HashFunction Hasher and HashCode work together for efficient streaming and one-shot hashing operations. Unlock Guava hashing today.

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: deep-dive
- Published: 2026-08-08

---

**Guava's hashing framework revolves around three core types—`HashFunction` as an immutable algorithm factory, `Hasher` as a mutable data sink, and `HashCode` as the immutable result—enabling both streaming and one-shot hashing operations.**

The `google/guava` library provides a robust hashing API that abstracts cryptographic and non-cryptographic algorithms through a fluent, type-safe interface. Understanding **Guava's Hashing utilities** allows developers to compute digests efficiently without managing the low-level complexity of Java's `MessageDigest` directly.

## The Three Core Abstractions of Guava Hashing

### HashFunction: The Immutable Algorithm Factory

A `HashFunction` represents a specific hashing algorithm and acts as a thread-safe factory for creating `Hasher` instances. Defined in [`com/google/common/hash/HashFunction.java`](https://github.com/google/guava/blob/main/com/google/common/hash/HashFunction.java), this interface declares key methods including `newHasher()`, `hashBytes(byte[])`, `hashString(CharSequence, Charset)`, and `bits()` which returns the bit length of the output. Concrete implementations such as `Murmur3_128HashFunction` and `MessageDigestHashFunction` encapsulate the actual algorithm logic while exposing a uniform contract.

### Hasher: The Mutable Data Sink

The `Hasher` interface, located in [`com/google/common/hash/Hasher.java`](https://github.com/google/guava/blob/main/com/google/common/hash/Hasher.java), extends `PrimitiveSink` and serves as a mutable accumulator that consumes data incrementally. Obtained via `HashFunction.newHasher()`, it provides methods like `putInt(int)`, `putLong(long)`, `putString(CharSequence, Charset)`, and `putBytes(byte[])` to stream data into the hash computation. Once all input is fed, calling `hash()` returns an immutable `HashCode` and effectively finalizes the operation.

### HashCode: The Immutable Result

`HashCode`, defined in [`com/google/common/hash/HashCode.java`](https://github.com/google/guava/blob/main/com/google/common/hash/HashCode.java), represents the finalized hash value as an immutable object. It offers conversion methods including `asInt()`, `asLong()`, `asBytes()`, and `toString()` for hexadecimal representation. Because it implements proper `equals()` and `hashCode()` contracts, it can safely serve as a key in `HashMap` instances or be persisted as a string for later comparison.

## Working with Guava's Hashing API

The typical workflow follows a clear sequence: select an algorithm, acquire a hasher, feed data, and extract the result.

1. **Select a hash algorithm** using static factory methods from [`com/google/common/hash/Hashing.java`](https://github.com/google/guava/blob/main/com/google/common/hash/Hashing.java):

```java
HashFunction hf = Hashing.sha256();          // Cryptographic
// or
HashFunction hf = Hashing.murmur3_128();     // Fast, non-cryptographic

```

2. **Create a hasher** from the function:

```java
Hasher hasher = hf.newHasher();

```

3. **Feed data** using the `PrimitiveSink` methods:

```java
hasher.putInt(42)
      .putString("hello", StandardCharsets.UTF_8)
      .putLong(123L);

```

4. **Obtain the result**:

```java
HashCode code = hasher.hash();

```

5. **Consume the hash** in your preferred format:

```java
int asInt = code.asInt();
long asLong = code.asLong();
byte[] bytes = code.asBytes();
String hex = code.toString();  // e.g., 64-character hex for SHA-256

```

For single-shot operations, skip the explicit `Hasher` creation using convenience methods:

```java
HashCode code = Hashing.sha256()
                       .hashString("payload", StandardCharsets.UTF_8);

```

### Practical Code Examples

The following examples demonstrate common patterns using the `google/guava` hashing utilities:

```java
// Example 1 – simple SHA-256 hash of a string
HashCode sha256 = Hashing.sha256()
                         .hashString("Guava is awesome!", StandardCharsets.UTF_8);
System.out.println("SHA-256 (hex): " + sha256.toString());

```

```java
// Example 2 – incremental hashing of mixed data
Hasher hasher = Hashing.murmur3_128().newHasher();
hasher.putInt(100)
      .putLong(0xdeadbeefL)
      .putString("foo", StandardCharsets.US_ASCII);
HashCode result = hasher.hash();
System.out.println("Murmur3-128 as long: " + result.asLong());

```

```java
// Example 3 – using the hash as a cache key
Map<HashCode, String> cache = new HashMap<>();
HashCode key = Hashing.fingerprint2011().hashString("my-resource", StandardCharsets.UTF_8);
cache.put(key, "cached value");
System.out.println("Lookup: " + cache.get(key));

```

## Available Hash Algorithms and Implementations

Guava distinguishes between cryptographic and non-cryptographic algorithms through distinct implementations in the `com/google/common/hash` package.

**Non-Cryptographic (Fast) Hashes:**

- `Murmur3_32HashFunction`: 32-bit variant suitable for hash tables
- `Murmur3_128HashFunction`: 128-bit variant for better distribution  
- `SipHashFunction`: Compact, fast, keyed hash function for hash-flood resistance

**Cryptographic Hashes:**

- `MessageDigestHashFunction`: Wrapper around Java's `MessageDigest` supporting MD5, SHA-1, and SHA-256

Use `Hashing.goodFastHash(int)` only for temporary, in-memory hashing where algorithm stability is not required. For persistent or cross-process hashing, prefer explicit algorithms like `murmur3_128()` or `sha256()`.

## Design Rationale and Performance Considerations

The separation of `HashFunction` and `Hasher` provides specific architectural benefits. `HashFunction` instances are immutable and thread-safe, allowing them to be stored as static constants and shared across threads. In contrast, `Hasher` maintains mutable state during the hashing process and should not be shared between threads.

When hashing large streams or complex objects, prefer the streaming `Hasher` API to avoid allocating intermediate byte arrays. This approach minimizes memory pressure by processing data incrementally rather than buffering entire inputs.

## Summary

- **`HashFunction`** acts as an immutable, thread-safe algorithm factory defined in [`HashFunction.java`](https://github.com/google/guava/blob/main/HashFunction.java)
- **`Hasher`** provides a mutable streaming interface for accumulating data, extending `PrimitiveSink` in [`Hasher.java`](https://github.com/google/guava/blob/main/Hasher.java)
- **`HashCode`** delivers immutable results with multiple output formats via `asInt()`, `asLong()`, `asBytes()`, and `toString()`
- Static factory methods in [`Hashing.java`](https://github.com/google/guava/blob/main/Hashing.java) provide access to implementations like `Murmur3_128HashFunction` and `MessageDigestHashFunction`
- Use cryptographic hashes (SHA-256) for security and non-cryptographic hashes (Murmur3) for performance
- One-shot hashing via `hashString()` or `hashBytes()` offers convenience, while the `Hasher` builder pattern optimizes large data processing

## Frequently Asked Questions

### What is the difference between HashFunction and Hasher in Guava?

`HashFunction` represents the algorithm itself and is immutable and thread-safe, responsible only for creating new `Hasher` instances via `newHasher()`. `Hasher` is the mutable accumulator that actually processes data through methods like `putInt()` and `putString()`, maintaining internal state until `hash()` is called to produce the final `HashCode`.

### Is Guava's HashCode thread-safe?

Yes, `HashCode` is immutable and thread-safe once created. It can be safely shared between threads, used as a key in concurrent hash maps, or cached without synchronization concerns.

### When should I use Murmur3 versus SHA-256 in Guava?

Use `Hashing.murmur3_128()` for high-performance, non-cryptographic hashing such as hash tables, caches, or checksums where speed is critical and security is not required. Use `Hashing.sha256()` or other `MessageDigest`-based functions when you need cryptographic security properties like collision resistance against malicious actors.

### How do I hash large files or streams with Guava?

Create a `Hasher` via `HashFunction.newHasher()` and feed the stream incrementally using `putBytes()` in a loop rather than loading the entire file into memory. This streaming approach prevents `OutOfMemoryError` on large inputs while computing the hash efficiently.