Module

std::collections::hash_map

HashMap

Deterministic open-addressed hash map with linear probing.

Overview

HashMap<K, V> stores keys and values in parallel contiguous buffers. Lookup uses:

  • deterministic hashing via std::hash::Hasher
  • open addressing
  • linear probing
  • tombstones for removed entries

The map grows when the configured load factor would be exceeded. Capacity is always managed in buckets, not bytes.

Key Requirements

Keys must satisfy Hash & Eq.

In practice that means user-defined key types usually provide:

  • @implements(Hash, Eq) on the record
  • a hash(&self, hasher: &mut Hasher): void method
  • an equals(&self, other: &K): boolean method

Bucket States

  state = 0  -> empty
  state = 1  -> occupied
  state = 2  -> tombstone

Empty buckets stop probing. Tombstones preserve probe chains after remove.

Memory Layout

  HashMap<K, V>
  ┌────────────────────────────────────────────────────────────┐
  │ states:  [E|O|T|O|...]                                    │
  │ hashes:  [0|h|0|h|...]                                    │
  │ keys:    [ |K| |K|...]                                    │
  │ values:  [ |V| |V|...]                                    │
  │ length: active occupied buckets                           │
  │ tombstones: removed buckets still participating in probe  │
  │ capacity: total bucket count                              │
  └────────────────────────────────────────────────────────────┘

keys and values are manual-storage buffers. Only buckets marked as occupied contain initialized logical elements.

Probe Sequence

A lookup starts at hash % capacity and advances one slot at a time until:

  • it finds an empty bucket (not present)
  • it finds a matching occupied bucket (present)
  • it has examined every bucket (full-table miss)
  capacity = 8
  start = hash % 8 = 3

  index:   0   1   2   3   4   5   6   7
  state:   E   T   O   O   T   O   E   E
                       ^
                       start

  probe order: 3 -> 4 -> 5 -> 6

  - slot 3: occupied, different key
  - slot 4: tombstone, continue
  - slot 5: occupied, different key
  - slot 6: empty, stop -> key does not exist

Insert / Replace Rules

Insert first reserves enough capacity, then probes exactly like lookup.

During probing it tracks:

  • the first tombstone encountered
  • whether an existing matching key was found

That gives three possible outcomes:

  1. matching key found -> replace value
  2. no key found, tombstone seen -> reuse first tombstone
  3. no key found, no tombstone seen -> use first empty bucket

Drop Behavior

HashMap owns both the key and the value stored in an occupied bucket.

That means:

  • replacing an entry drops the previous key and returns the previous value
  • removing an entry drops the key and returns the value
  • clear() drops every occupied key/value pair
  • drop() calls clear() and then frees the backing buffers

Tombstones do not hold logical values anymore; they only preserve probing structure until the next rehash.

Complexity

Average-case complexity:

  • insert -> O(1)
  • get -> O(1)
  • remove -> O(1)
  • reserve -> O(n) when rehashing occurs

Worst-case probe chains are linear in the bucket count.

Example

import Eq from "std::collections";
import Hash from "std::collections";
import HashMap from "std::collections";
import Hasher from "std::hash";

@implements(Hash, Eq)
record Key {
  id: i32;

  hash(&self, hasher: &mut Hasher): void {
    let mut state: &mut Hasher = hasher;
    state.writeI32(self.id);
  }

  equals(&self, other: &Key): boolean {
    return self.id == other.id;
  }
}

function main(): i32 {
  let mut map: HashMap<Key, i32> = HashMap::new<Key, i32>();
  map.insert(Key { id: 1 }, 10);
  map.insert(Key { id: 2 }, 20);

  let lookup: Key = Key { id: 2 };
  let value: i32 = match (map.get(&lookup)) {
    Option::SOME(found) -> *found,
    Option::NONE -> -1,
  };

  return value;
}

Records

record HashMap<K, V>

Deterministic open-addressed map from K to V.

This container owns both keys and values. Replacing or removing an entry drops the previous key/value exactly once.

Members
states: *mut u8
hashes: *mut u64
keys: *mut K
values: *mut V
length(&self): u64

Returns the number of occupied entries currently stored.

capacity(&self): u64

Returns the number of buckets currently allocated.

tombstones: u64
static init(capacity: u64): HashMap<K, V>

Compatibility alias for HashMap::new(capacity).

static init(capacity: u64): HashMap<K, V>

Compatibility alias for HashMap::new(capacity).

static new(capacity: u64): HashMap<K, V>

Creates an empty map with enough buckets for at least capacity logical entries at the current load-factor policy.

Example

import HashMap from "std::collections";

let map: HashMap<i32, i32> = HashMap::new<i32, i32>(64);
static new(capacity: u64): HashMap<K, V>

Creates an empty map with enough buckets for at least capacity logical entries at the current load-factor policy.

Example

import HashMap from "std::collections";

let map: HashMap<i32, i32> = HashMap::new<i32, i32>(64);
capacity(&self): u64

Returns the number of buckets currently allocated.

clear(&mut self): void

Drops all occupied entries but keeps the bucket allocation for reuse.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.insert(1, 10);
map.clear();
contains(&self, key: &K): boolean

Returns true if key is present in the map.

This probes directly without materializing an intermediate Option<&V>.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.insert(1, 10);
let found: boolean = map.contains(&1);
containsKey(&self, key: &K): boolean

Compatibility alias for contains.

drop(&mut self): void

Drops all entries and releases all backing storage.

entryKey(&self, cursor: u64): &K

Returns the key for a cursor previously returned by the entry cursor API.

entryValue(&self, cursor: u64): &V

Returns the value for a cursor previously returned by the entry cursor API.

firstEntryCursor(&self): Option<u64>

Returns the first occupied entry cursor in deterministic bucket order.

Cursors are opaque bucket positions that remain valid until the map is mutated. Callers can advance them with nextEntryCursor and read the pointed entry via entryKey / entryValue.

freeStorage(&mut self): void

Releases raw storage buffers and resets logical counters.

get(&self, key: &K): Option<&V>

Returns an immutable reference to the value for key, if present.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.insert(7, 99);
let value: Option<&i32> = map.get(&7);
getMut(&mut self, key: &K): Option<&mut V>

Returns a mutable reference to the value for key, if present.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.insert(7, 99);
let value: Option<&mut i32> = map.getMut(&7);
insert(&mut self, key: K, value: V): Option<V>

Inserts or replaces the value for key.

Returns:

  • Option::NONE when the key was not present
  • Option::SOME(previous) when an existing value was replaced

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
let inserted: Option<i32> = map.insert(1, 10); // NONE
let replaced: Option<i32> = map.insert(1, 20); // SOME(10)
isEmpty(&self): boolean

Returns true when the map contains no occupied entries.

length(&self): u64

Returns the number of occupied entries currently stored.

nextEntryCursor(&self, cursor: u64): Option<u64>

Returns the next occupied entry cursor after cursor, if any.

rehash(&mut self, newCapacity: u64): void

Rebuilds the table into a new bucket array.

remove(&mut self, key: &K): Option<V>

Removes key from the map and returns its previous value, if present.

Removal leaves a tombstone bucket behind so probe chains remain valid.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.insert(1, 10);
let removed: Option<i32> = map.remove(&1);
reserve(&mut self, additional: u64): void

Ensures enough buckets exist to insert additional more entries without violating the current load-factor policy.

Example

import HashMap from "std::collections";

let mut map: HashMap<i32, i32> = HashMap::new<i32, i32>();
map.reserve(32);

Traits

trait Hash

Hashing contract required by HashMap and HashSet keys.

Implementations must write the same hash bytes for values that compare equal. The trait is local to collections so key types can implement it without importing the broader std::hash::Hash surface directly.

Members
hash(&self): void

Feeds a stable representation of self into the provided hasher.

Functions

function __hashMapStorageBytes<T>(count: u64): u64

Computes how many bytes must be reserved to store count elements of T.

Zero-sized element types still reserve one byte so pointer storage remains non-null and addressable by the implementation.

Constants

const EMPTY_STATE: u8
const INITIAL_BUCKET_CAPACITY: u64
const MAX_LOAD_PERCENT: u64
const OCCUPIED_STATE: u8
const TOMBSTONE_STATE: u8