Theory

Three parts in pseudocode

Three parts: the hash function, collision handling, and growth.

The hash function

FNV-1a — one of the simplest good ones. One xor and one multiply per byte:

hashKey(s):
    h ← 14695981039346656037        // offset basis
    for each byte b of s:
        h ← h XOR b
        h ← h * 1099511628211       // prime
    return h

The bucket number is hashKey(title) % len(buckets).

It works on BYTES, and for a hash table that is correct. Two identical strings have identical bytes whatever alphabet they are written in; two different ones almost certainly differ. The function does not need to know what a letter is.

In step 7 you will meet a structure for which bytes are not enough — and why.

Collisions: chains

Two different keys can land in the same bucket. The simplest fix: each bucket holds a chain, and a lookup walks it.

Get(title):
    i ← hashKey(title) % len(buckets)
    for e from buckets[i] along the chain:
        c.Hit()
        if e.key == title: return e.item, true
    return empty, false

The comparison count is the chain length. That is why the counter matters so much here: it measures precisely the quantity that decides whether the O(1) is real.

An equal key is not a collision

This is easy to conflate, so plainly:

  • a collision is different keys landing in the same bucket. Unavoidable, handled by the chain;
  • an equal key is the same title stored twice. That is not a collision; Put must replace the existing entry.

In lesson 1 you wrote gen -dup-rate. It produces equal keys, not collisions — and step 6 shows what it actually does to the index.

Growth

As items accumulate without more buckets, chains lengthen and the O(1) becomes a lie.

The load factor is items ÷ buckets. When it reaches 1.0, the bucket count doubles and everything is rehashed:

grow():
    old ← buckets
    buckets ← a new array, twice the size
    for every entry in old: insert it again using hashKey % the new size

That is an O(n) operation. But it happens rarely — exactly like lesson 2's append, and for the same reason. Amortization is the same idea here.