4.13 Hash Tables
Hash Tables in The Racket Guide introduces hash tables.
A hash table (or simply hash) maps each of its keys to a single value. For a given hash table, keys are equivalent via equal?, eqv?, or eq?, and keys are retained either strongly or weakly (see Weak Boxes). A hash table is also either mutable or immutable. Immutable hash tables support effectively constant-time access and update, just like mutable hash tables; the constant on immutable operations is usually larger, but the functional nature of immutable hash tables can pay off in certain algorithms.
Immutable hash tables actually provide O(log N) access and update. Since N is limited by the address space so that log N is limited to less than 30 or 62 (depending on the platform), log N can be treated reasonably as a constant.
For equal?-based hashing, the built-in hash functions on strings, pairs, lists, vectors, prefab or transparent structures, etc., take time proportional to the size of the value. The hash code for a compound data structure, such as a list or vector, depends on hashing each item of the container, but the depth of such recursive hashing is limited (to avoid potential problems with cyclic data). For a non-list pair, both car and cdr hashing is treated as a deeper hash, but the cdr of a list is treated as having the same hashing depth as the list.
A hash table can be used as a two-valued sequence (see Sequences). The keys and values of the hash table serve as elements of the sequence (i.e., each element is a key and its associated value). If a mapping is added to or removed from the hash table during iteration, then an iteration step may fail with exn:fail:contract, or the iteration may skip or duplicate keys and values. See also in-hash, in-hash-keys, in-hash-values, and in-hash-pairs.
Two hash tables cannot be equal? unless they use the same key-comparison procedure (equal?, eqv?, or eq?), both hold keys strongly or weakly, and have the same mutability.
Caveats concerning concurrent modification: A mutable hash table can be manipulated with hash-ref, hash-set!, and hash-remove! concurrently by multiple threads, and the operations are protected by a table-specific semaphore as needed. Three caveats apply, however:
If a thread is terminated while applying hash-ref, hash-set!, hash-remove!, hash-ref!, or hash-update! to a hash table that uses equal? or eqv? key comparisons, all current and future operations on the hash table may block indefinitely.
The hash-map, hash-for-each, and hash-clear! procedures do not use the table’s semaphore to guard the traversal as a whole. Changes by one thread to a hash table can affect the keys and values seen by another thread part-way through its traversal of the same hash table.
The hash-update! and hash-ref! functions use a table’s semaphore independently for the hash-ref and hash-set! parts of their functionality, which means that the update as a whole is not “atomic.”
Caveat concerning mutable keys: If a key in an equal?-based hash table is mutated (e.g., a key string is modified with string-set!), then the hash table’s behavior for insertion and lookup operations becomes unpredictable.
A literal or printed hash table starts with #hash, #hasheqv, or #hasheq. See Reading Hash Tables for information on reading hash tables and Printing Hash Tables for information on printing hash tables.
procedure
(hash-equal? hash) → boolean?
hash : hash?
procedure
(hash-weak? hash) → boolean?
hash : hash?
procedure
(hash key val ... ...) → (and/c hash? hash-equal? immutable?)
key : any/c val : any/c
procedure
(hasheq key val ... ...) → (and/c hash? hash-eq? immutable?)
key : any/c val : any/c
procedure
(hasheqv key val ... ...) → (and/c hash? hash-eqv? immutable?)
key : any/c val : any/c
The hash procedure creates a table where keys are compared with equal?, hasheq procedure creates a table where keys are compared with eq?, and hasheqv procedure creates a table where keys are compared with eqv?.
The key to val mappings are added to the table in the order that they appear in the argument list, so later mappings can hide earlier mappings if the keys are equal.
procedure
(make-hash [assocs]) → (and/c hash? hash-equal?)
assocs : (listof pair?) = null
procedure
(make-hasheqv [assocs]) → (and/c hash? hash-eqv?)
assocs : (listof pair?) = null
procedure
(make-hasheq [assocs]) → (and/c hash? hash-eq?)
assocs : (listof pair?) = null
The make-hash procedure creates a table where keys are compared with equal?, make-hasheq procedure creates a table where keys are compared with eq?, and make-hasheqv procedure creates a table where keys are compared with eqv?.
The table is initialized with the content of assocs. In each element of assocs, the car is a key, and the cdr is the corresponding value. The mappings are added to the table in the order that they appear in assocs, so later mappings can hide earlier mappings.
See also make-custom-hash.
procedure
(make-weak-hash [assocs]) → (and/c hash? hash-equal? hash-weak?)
assocs : (listof pair?) = null
procedure
(make-weak-hasheqv [assocs]) → (and/c hash? hash-eqv? hash-weak?)
assocs : (listof pair?) = null
procedure
(make-weak-hasheq [assocs]) → (and/c hash? hash-eq? hash-weak?)
assocs : (listof pair?) = null
Beware that values in the table are retained normally. If a value in the table refers back to its key, then the table will retain the value and therefore the key; the mapping will never be removed from the table even if the key becomes otherwise inaccessible. To avoid that problem, instead of mapping the key to the value, map the key to an ephemeron that pairs the key and value.
procedure
(make-immutable-hash [assocs])
→ (and/c hash? hash-equal? immutable?) assocs : (listof pair?) = null
procedure
(make-immutable-hasheqv [assocs])
→ (and/c hash? hash-eqv? immutable?) assocs : (listof pair?) = null
procedure
(make-immutable-hasheq [assocs])
→ (and/c hash? hash-eq? immutable?) assocs : (listof pair?) = null
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-set*! hash key v ... ...) → void?
hash : (and/c hash? (not/c immutable?)) key : any/c v : any/c
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-set hash key v) → (and/c hash? immutable?)
hash : (and/c hash? immutable?) key : any/c v : any/c
See also the caveat concerning mutable keys above.
procedure
(hash-set* hash key v ... ...) → (and/c hash? immutable?)
hash : (and/c hash? immutable?) key : any/c v : any/c
See also the caveat concerning mutable keys above.
procedure
hash : hash? key : any/c
failure-result : (failure-result/c any/c) =
(lambda () (raise (make-exn:fail:contract ....)))
If failure-result is a procedure, it is called (through a tail call) with no arguments to produce the result.
Otherwise, failure-result is returned as the result.
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-has-key? hash key) → boolean?
hash : hash? key : any/c
procedure
(hash-update! hash key updater [ failure-result]) → void? hash : (and/c hash? (not/c immutable?)) key : any/c updater : (any/c . -> . any/c)
failure-result : (failure-result/c any/c) =
(lambda () (raise (make-exn:fail:contract ....)))
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-update hash key updater [failure-result])
→ (and/c hash? immutable?) hash : (and/c hash? immutable?) key : any/c updater : (any/c . -> . any/c)
failure-result : (failure-result/c any/c) =
(lambda () (raise (make-exn:fail:contract ....)))
See also the caveat concerning mutable keys above.
procedure
(hash-remove! hash key) → void?
hash : (and/c hash? (not/c immutable?)) key : any/c
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-remove hash key) → (and/c hash? immutable?)
hash : (and/c hash? immutable?) key : any/c
See also the caveat concerning mutable keys above.
procedure
(hash-clear! hash) → void?
hash : (and/c hash? (not/c immutable?))
If hash is not an impersonator, then all mappings are removed in constant time. If hash is an impersonator, then each key is removed one-by-one using hash-remove!.
See also the caveats concerning concurrent modification and the caveat concerning mutable keys above.
procedure
(hash-clear hash) → (and/c hash? immutable?)
hash : (and/c hash? immutable?)
If hash is not a chaperone, then clearing is equivalent to creating a new hash table, and the operation is performed in constant time. If hash is a chaperone, then each key is removed one-by-one using hash-remove.
procedure
(hash-copy-clear hash) → hash?
hash : hash?
If a hash table is extended with new keys (either through proc or by another thread) while a hash-map or hash-for-each traversal is in process, arbitrary key–value pairs can be dropped or duplicated in the traversal. Key mappings can be deleted or remapped (by any thread) with no adverse affects; the change does not affect a traversal if the key has been seen already, otherwise the traversal skips a deleted key or uses the remapped key’s new value.
If try-order? is true, then the order of keys and values passed to proc is normalized under certain circumstances, such as when the keys are all symbols and hash is not an impersonator.
See also the caveats concerning concurrent modification above.
Changed in version 6.3 of package base: Added the try-order? argument.
procedure
(hash-values hash) → (listof any/c)
hash : hash?
procedure
(hash-for-each hash proc [try-order?]) → void?
hash : hash? proc : (any/c any/c . -> . any) try-order? : any/c = #f
Changed in version 6.3 of package base: Added the try-order? argument.
procedure
(hash-count hash) → exact-nonnegative-integer?
hash : hash?
procedure
(hash-empty? hash) → boolean?
hash : hash?
procedure
(hash-iterate-first hash)
→ (or/c #f exact-nonnegative-integer?) hash : hash?
procedure
(hash-iterate-next hash pos)
→ (or/c #f exact-nonnegative-integer?) hash : hash? pos : exact-nonnegative-integer?
procedure
(hash-iterate-key hash pos) → any
hash : hash? pos : exact-nonnegative-integer?
procedure
(hash-iterate-value hash pos) → any
hash : hash? pos : exact-nonnegative-integer?
procedure
(eq-hash-code v) → fixnum?
v : any/c
procedure
(eqv-hash-code v) → fixnum?
v : any/c
procedure
(equal-hash-code v) → fixnum?
v : any/c
procedure
v : any/c
4.13.1 Additional Hash Table Functions
(require racket/hash) | package: base |
procedure
(hash-union h0 h ... [ #:combine combine #:combine/key combine/key]) → (and/c hash? hash-can-functional-set?) h0 : (and/c hash? hash-can-functional-set?) h : hash?
combine : (-> any/c any/c any/c) = (lambda _ (error 'hash-union ....))
combine/key : (-> any/c any/c any/c any/c) = (lambda (k a b) (combine a b))
> (hash-union (make-immutable-hash '([1 . one])) (make-immutable-hash '([2 . two])) (make-immutable-hash '([3 . three]))) '#hash((1 . one) (3 . three) (2 . two))
> (hash-union (make-immutable-hash '([1 one uno] [2 two dos])) (make-immutable-hash '([1 ein une] [2 zwei deux])) #:combine/key (lambda (k v1 v2) (append v1 v2))) '#hash((1 . (one uno ein une)) (2 . (two dos zwei deux)))
procedure
(hash-union! h0 h ... [ #:combine combine #:combine/key combine/key]) → void? h0 : (and/c hash? hash-mutable?) h : hash?
combine : (-> any/c any/c any/c) = (lambda _ (error 'hash-union ....))
combine/key : (-> any/c any/c any/c any/c) = (lambda (k a b) (combine a b))
> (define h (make-hash))
> h '#hash()
> (hash-union! h (make-immutable-hash '([1 one uno] [2 two dos])))
> h '#hash((2 . (two dos)) (1 . (one uno)))
> (hash-union! h (make-immutable-hash '([1 ein une] [2 zwei deux])) #:combine/key (lambda (k v1 v2) (append v1 v2)))
> h '#hash((2 . (two dos zwei deux)) (1 . (one uno ein une)))