Expected O(1), not a guarantee
Java 21 documents constant-time get/put only when hashes disperse entries across buckets. The counters here are modeled operations, not runtime.
A deterministic semantic trace of hashCode, spread, mask, bucket lookup, equals, mutation, resize split, and verified tree-bin gates. This is a teaching model of Java 21 behavior, not a JVM profiler.
Constructor input is rounded to a power-of-two allocation target. The table remains null until the first put.
Configure the constructor, then run a put/get/remove operation.
raw = (key == null) ? 0 : key.hashCode() hash = raw ^ (raw >>> 16)
Boundary: JDK 21 behavior, rendered as a semantic fragment rather than source line numbers.
Run an operation to create semantic micro-steps.
No steps.
threshold=4 currently means allocation target, not resize threshold.
The table is modeled as contiguous reference slots. Entry cards expose hash/key/value/next fields but make no claim about object headers, compressed references, padding, physical placement, registers, or cache outcomes.
Run an operation to attach an invariant receipt to every step.
Counts semantic work only. These are not nanoseconds, CPU cycles, allocations in bytes, or cache misses.
No operation work yet.
Illustrative review snippets. This lab does not compile or execute them.
final class AccountKey {
String accountId;
String email;
public boolean equals(Object other) {
return other instanceof AccountKey k
&& accountId.equals(k.accountId);
}
public int hashCode() {
return email.hashCode(); // different field: broken
}
}equals uses accountId, but hashCode uses email. Equal objects can enter different buckets.
Complexity and memory statements below are analytical or modeled, never measured runtime.
Java 21 documents constant-time get/put only when hashes disperse entries across buckets. The counters here are modeled operations, not runtime.
Capacity is the reference-array length. First allocation computes a Java-float product; from capacity 16 onward OpenJDK doubles the previous threshold when doubling the table. Before allocation the field holds the rounded target.
A power-of-two capacity makes capacity−1 a low-bit mask. The spread step folds high hash bits down before the AND.
One resize scans the old table and relinks existing entries, so that put is expensive. Across geometric growth, the occasional work is amortized over many inserts.
HashMap stores the spread hash computed at insertion. If the same key object later reports a different hashCode, lookup can start in another bucket.
Many colliding keys increase comparisons and pointer traversal. Tree bins are conditional mitigation, not cryptographic hashing or a universal worst-case guarantee.
The model separates one reference array from entry objects with key/value/next fields. Pointer chasing can reduce locality, but this lab does not observe cache misses or object byte size.
TreeNodes add links and balancing work. OpenJDK reserves them for sufficiently large bins and tables; exact removal untreeification depends on tree shape.
A replay is deterministic for teaching, but Java 21 HashMap explicitly makes no guarantee that encounter order stays constant.
This change implements HashMap only. Every other item below remains a roadmap entry, not a shipped claim.