Java Collections + Hashing

How does HashMap work internally?

HashMap stores key-value pairs in buckets. It uses the key's hash code to find a bucket, handles collisions inside that bucket, and resizes when the map becomes too full.

CollectionsHashingCore Java

The Short Answer

A HashMap stores key-value pairs in an internal array of buckets.

When you call put(key, value), Java uses the key's hash code to decide which bucket the entry should go into. When you call get(key), Java uses the same idea to quickly jump to the likely bucket instead of scanning every entry.

The whole point of HashMap is this: use hashing so lookup is usually close to O(1), instead of searching linearly through all entries.

The Mental Model

Key → hashCode → bucket index

Key

"user:42"

hashCode()

numeric hash

bucket index

array slot

Bucket 0
Bucket 1
Entry
Bucket 2
Bucket 3

What Happens During put()?

  1. HashMap receives the key and value.
  2. It calculates a hash from the key.
  3. It converts that hash into a bucket index.
  4. If the bucket is empty, it stores the entry there.
  5. If the bucket already has entries, it checks whether the key already exists using equals().
  6. If the key exists, the old value is replaced. If not, a new entry is added to that bucket.
java
Map<String, Integer> scores = new HashMap<>();

scores.put("alice", 90);
scores.put("bob", 85);
scores.put("alice", 95); // replaces Alice's old value

Why hashCode() and equals() Matter Together

HashMap uses hashCode() to find the bucket, but it uses equals() to confirm whether two keys are actually the same logical key.

Interview shortcut: hashCode() helps find the right neighborhood. equals() identifies the exact house.
java
class UserId {
    private final String value;

    UserId(String value) {
        this.value = value;
    }

    // If two UserId objects are logically equal,
    // equals() and hashCode() must agree.
}

What Is a Collision?

A collision happens when two different keys land in the same bucket. This is normal. HashMap is designed to handle it.

Two keys, same bucket

Bucket 0
Bucket 1
key = "alice"
key = "charlie"
Bucket 2

If collisions are rare, HashMap remains very fast. If many keys collide into the same bucket, lookup becomes slower because Java has to search within that bucket.

Linked List vs Tree Bin

In older mental models, people often say: “A HashMap bucket contains a linked list.” That is partly true, but modern Java has an extra optimization.

If too many entries collide into the same bucket, Java can convert that bucket from a linked structure into a tree structure. This improves worst-case lookup behavior when collisions are heavy.

Normal Collision Handling

Bucket
Node → Node → Node

Heavy Collision Case

Bucket
Tree Bin

Why Resizing Happens

HashMap has a capacity, which is the number of buckets. It also has a load factor, which controls how full the map is allowed to get before it grows.

The common default load factor is 0.75. So if the capacity is 16, resizing happens after the number of entries exceeds 12.

java
capacity = 16
loadFactor = 0.75

threshold = capacity * loadFactor
threshold = 16 * 0.75 = 12

When the threshold is exceeded, HashMap creates a larger internal table and redistributes entries into new buckets.

Resizing Visualized

Before Resize

B0
B1
B2
B3
B4
B5
B6
B7

Buckets are getting crowded. Collisions become more likely.

After Resize

B0
B1
B2
B3
B4
B5
B6
B7
B8
B9
B10
B11
B12
B13
B14
B15

More buckets reduce crowding, but resizing itself has a cost.

The Interview-Friendly Explanation

HashMap uses an array of buckets. It hashes the key to find a bucket index. If multiple keys land in the same bucket, that is a collision, and HashMap stores multiple entries in that bucket. When the map gets too full, it resizes and redistributes entries. Good hashCode and equals implementations are essential for correctness and performance.

Common Interview Follow-Ups

Why is HashMap usually O(1)?

Because hashing lets Java jump directly to a bucket instead of scanning every entry. This assumes the hash function distributes keys well.

What happens if many keys have the same hash?

They collide into the same bucket. Lookup becomes slower because Java must search within that bucket.

Why must equals() and hashCode() be consistent?

If two objects are equal, they must produce the same hash code. Otherwise, HashMap may look in the wrong bucket and fail to find a logically equal key.

Why is resizing expensive?

Because HashMap must allocate a larger internal table and redistribute entries into new bucket positions.

Final Takeaway

HashMap feels simple from the outside, but internally it is a careful balance between hashing, bucket distribution, collision handling, resizing, memory usage, and lookup speed.