Hash-based key-value storage parameterized by key and value schemas.


HashTable

Construct with key and value schemas before the type name: Key Value HashTable, for example Int32 Int32 HashTable.

HashTable

(keySchema valueSchema -- table) Constructs an empty owning table from key and value schemas.
  • Use schema objects such as Int32 or mutable prototype views such as @keyPrototype @valuePrototype; immutable prototype views can make find or at fail to compile when lookup instantiates the matching-node branch («set», Second argument (target) is of different schema). Use schema objects or mutable prototype views.
  • Stored key and value variables must support construction. Text is not a constructible stored item here; use String for owned text.

Fields

A new HashTable is empty. An immutable table source is copied using contained assignment operations; a mutable table can be moved, leaving the source empty. Destroying the table destroys its owned arrays, nodes, keys, and values, not objects merely referenced by contained reference fields.

Buckets have independent array storage. An insertion can move a bucket and invalidate its references even without changing the bucket count. Erasing can replace another slot in the same bucket. Reacquire references and iterators after any insertion or erasure, and after clear, release, assignment, or destruction.

iter

(-- iter) Returns an iterator of stored {key value} pairs.
  • next has effect (-- item valid). It yields an immutable key view and a value view whose mutability follows the table view.

keys

(-- iter) Returns an iterator of stored keys.
  • next has effect (-- item valid); it always yields immutable key views.

values

(-- iter) Returns an iterator of stored values.
  • next has effect (-- item valid); it yields value views whose mutability follows the table view.

size

(-- count) Returns the number of stored key-value pairs.

at

(key -- ref) Returns a view of the stored value, mutable through a mutable table and immutable through an immutable table.
  • A missing key reports Key is not in the collection with DEBUG enabled and exits with status 2; with DEBUG disabled it yields a NIL value reference that must not be dereferenced.

find

(key -- result) Returns a result record describing the key lookup.
  • result.success is TRUE when the key exists. result.value is a value reference whose mutability follows the table view; ordinary reads of a result field are immutable, while @r.@value retains the stored reference's view. The value reference is NIL when success is FALSE.

erase

(key --) Removes the stored pair with key.
  • On a nonempty table, a missing key fails with Erasing unexisting element! and terminates with status 2. On an empty table, erase is a no-op.

insert

(key value --) Constructs a stored pair from key and value.
  • With DEBUG enabled, inserting an existing key fails with Inserting existing element!; with DEBUG disabled, duplicate pairs are accepted.
  • Each input is copied or moved according to its view and construction support; mutable managed inputs can be moved from, immutable inputs are copied, and contained references retain aliasing.

insertUnsafe

(key value --) Inserts a pair without checking for an existing key.
  • Construction, growth, and invalidation follow insert; check for an existing key when unique keys are required.

rebuild

(bucketCount --) Redistributes the stored nodes over bucketCount buckets; the size counter is not changed.
  • bucketCount must be a positive Int32.
  • Normal growth uses powers of two (16, 32, ...). The bucket index is hash and (bucketCount - 1), so a count that is not a power of two leaves some buckets unused.
  • rebuild invalidates stored references and iterators.

Known issue: shrinking the bucket count destroys truncated buckets before rehashing, loses their pairs without correcting size, and leaves the table inconsistent; do not shrink the bucket count.

clear

(--) Removes stored pairs and sets the item count to zero.

Remarks

  • The outer bucket array retains its reserved allocation; stored pairs and their owned values are destroyed.

release

(--) Releases bucket storage and sets the item count to zero.

Implementation helper

makeIter

(buckets projection -- iter) Used by iter, keys, and values; builds an iterator over bucket-node storage, applying projection to each node.
  • The iterator retains the supplied storage view and has method, data, bucket, item, and next fields.
  • projection must consume a node and return one item; it is stored as a static field.
  • next has effect (-- item valid). On exhaustion it calls projection with a node containing NIL key/value references and returns its result followed by FALSE. Do not dereference those NIL references.

Key requirements and iteration order


hash

(object -- hashValue) Casts an integer input to Nat32, or returns the result of the input's hash field or method without converting it.

toHashTable

(source -- hashTable) Builds an owning HashTable from an iteration source of key-value pairs.
  • Each item is a Struct of two fields (or a view of one): field 0 is the key and field 1 is the value. Field names are ignored; a Dict must declare those fields in that order. A Tuple such as (1 10) and the pairs yielded by another table's iter are accepted.
  • The first item returned by next supplies both schemas, even if its validity flag is FALSE; valid items are inserted in source order.
  • An empty source works when its end item supplies both field schemas. Duplicate handling is the same as insert.

Examples

Lookup and erasure

"HashTable" use
"String"    use
"control"   use

{} Int32 {} [
  h: Int32 Int32 HashTable;
  1 10 @h.insert
  2 20 @h.insert
  3 30 @h.insert
  r: 3 @h.find;
  ("size=" h.size LF
    "at2=" 2 @h.at new LF
    "found3=" r.success LF
    "value3=" @r.value new LF) printList
  2 @h.erase
  ("size2=" h.size LF) printList
  0
] "main" exportFunction

Expected Output

size=3
at2=20
found3=TRUE
value3=30
size2=2

Lookup of a missing key

"HashTable" use
"String"    use
"control"   use

{} Int32 {} [
  h: Int32 Int32 HashTable;
  1 10 @h.insert
  r0: 1 @h.find;
  r1: 5 @h.find;
  ("found0=" r0.success LF
    "value0=" @r0.value new LF
    "found1=" r1.success LF
    "size=" h.size LF) printList
  0
] "main" exportFunction

Expected Output

found0=TRUE
value0=10
found1=FALSE
size=1

Building from pairs

"HashTable" use
"String"    use
"control"   use

{} Int32 {} [
  t: ((1 10) (2 20)) toHashTable;
  ("size=" t.size LF
    "at2=" 2 @t.at new LF) printList
  0
] "main" exportFunction

Expected Output

size=2
at2=20

Runtime example: iter, keys, and values

"HashTable" use
"String"    use
"control"   use

{} Int32 {} [
  h: Int32 Int32 HashTable;
  1 10 @h.insert
  2 20 @h.insert
  pairs: @h.iter;
  pair0: ok0: @pairs.next;;
  pair1: ok1: @pairs.next;;
  pair2: ok2: @pairs.next;;
  keys: @h.keys;
  key0: hasKey0: @keys.next;;
  key1: hasKey1: @keys.next;;
  values: @h.values;
  value0: hasValue0: @values.next;;
  value1: hasValue1: @values.next;;
  ("pair0Key=" pair0.key LF
    "pair0Value=" @pair0.value new LF
    "pair1Key=" pair1.key LF
    "pair1Value=" @pair1.value new LF
    "iterDone=" ok2 LF
    "key0=" key0 LF
    "key1=" key1 LF
    "value0=" value0 new LF
    "value1=" value1 new LF) printList
  0
] "main" exportFunction

Expected Output

pair0Key=1
pair0Value=10
pair1Key=2
pair1Value=20
iterDone=FALSE
key0=1
key1=2
value0=10
value1=20

See also