Hashtbl is a reimplementation of the standard
MoreLabels.Hashtbl. Its worst case time complexity is O(log(N)) for lookups and
additions, unlike the standard MoreLabels.Hashtbl, which is O(N).
A hash table is implemented as an array of AVL trees (see Avltree). If
growth_allowed (default true) is false then size is the final size of the array;
the table can always hold more elements than size, but they will all go into
tree nodes. If it is true (default) then the array will double in size when the number
of elements in the table reaches twice the size of the array. When this happens, all
existing elements will be reinserted, which can take a long time. If you care about
latency, set size and growth_allowed=false if possible.
In most cases, functions passed as arguments to hash table accessors must not mutate
the hash table while it is being accessed, as this will result in an exception. For
example, iter and change take a function f which must not modify t. In a few
cases, mutation is allowed, such as in Hashtbl.find_and_call, where all access to
t is finished before the ~if_found and ~if_not_found arguments are invoked.
We have three kinds of hash table modules:
HashtblHashtbl.PolyKey.Table (a class of similar modules)There are three kinds of hash-table functions:
create, of_alist)t_of_sexp, sexp_of_t, and bin_io too)fold, mem, find, map, filter_map, ...)Here is a table showing what classes of functions are available in each kind of hash-table module:
creation sexp-conv accessors
Hashtbl X
Hashtbl.Poly X X
Key.Table X X X'The entry marked with X' is there for historical reasons, and may be eliminated at
some point. The upshot is that one should use Hashtbl for accessors, Hashtbl.Poly
for hash-table creation and sexp conversion using polymorphic compare/hash, and
Key.Table for hash-table creation and sexp conversion using Key.compare and
Key.hash.
For many students of OCaml, using hashtables is complicated by the functors. Here are a few tips:
To create a hashtable with string keys use String.Table:
let table = String.Table.create () ~size:4 in
List.iter ~f:(fun (key, data) -> Hashtbl.set table ~key ~data)
[ ("A", 1); ("B", 2); ("C", 3); ];
Hashtbl.find table "C"Here 4 need only be a guess at the hashtable's future size. There are other similar
pre-made hashtables, e.g., Int63.Table or Host_and_port.Table.
To create a hashtable with a custom key type use Hashable:
module Key = struct
module T = struct
type t = String.t * Int63.t [@@deriving compare, hash, sexp]
end
include T
include Hashable.Make (T)
end
let table = Key.Table.create () ~size:4 in
List.iter ~f:(fun (key, data) -> Hashtbl.set table ~key ~data)
[ (("pi", Int63.zero), 3.14159);
(("e", Int63.minus_one), 2.71828);
(("Euler", Int63.one), 0.577215);
];
Hashtbl.find table ("pi", Int63.zero)Performance may improve if you define equal and hash explicitly, e.g.:
let equal (x, y) (x', y') = String.(=) x x' && Int63.(=) y y'
let hash (x, y) = String.hash x + Int63.hash y * 65599include Hashtbl_intf.Hashtbl with type ('a, 'b) t = ('a, 'b) Base.Hashtbl.ttype ('a, 'b) tWe use [@@deriving_inline sexp_of][@@@end] but not [@@deriving sexp] because we want people to
be explicit about the hash and comparison functions used when creating hashtables.
One can use Hashtbl.Poly.t, which does have [@@deriving_inline sexp][@@@end], to use
polymorphic comparison and hashing.
include sig ... endval sexp_of_t : ('a ‑> Base.Sexp.t) ‑> ('b ‑> Base.Sexp.t) ‑> ('a, 'b) t ‑> Base.Sexp.tinclude Base.Invariant.S2 with type (a, b) t := (a, b) tval invariant : 'a Base__.Invariant_intf.inv ‑> 'b Base__.Invariant_intf.inv ‑> ('a, 'b) t Base__.Invariant_intf.invinclude Hashtbl_intf.Hashtbl_intf.Creators with type (a, b) t := (a, b) t with type 'a key = 'a with type (a, b, z) create_options := (a, b, z) Hashtbl_intf.Hashtbl_intf.create_options_with_first_class_moduleval create : ('a key, 'b, unit ‑> ('a, 'b) t) create_optionsval of_alist : ('a key, 'b, ('a key * 'b) list ‑> [ `Ok of ('a, 'b) t | `Duplicate_key of 'a key ]) create_optionsval of_alist_report_all_dups : ('a key, 'b, ('a key * 'b) list ‑> [ `Ok of ('a, 'b) t | `Duplicate_keys of 'a key list ]) create_optionsval of_alist_or_error : ('a key, 'b, ('a key * 'b) list ‑> ('a, 'b) t Base.Or_error.t) create_optionsval of_alist_exn : ('a key, 'b, ('a key * 'b) list ‑> ('a, 'b) t) create_optionsval of_alist_multi : ('a key, 'b list, ('a key * 'b) list ‑> ('a, 'b list) t) create_optionsval create_mapped : ('a key, 'b, get_key:('r ‑> 'a key) ‑> get_data:('r ‑> 'b) ‑> 'r list ‑> [ `Ok of ('a, 'b) t | `Duplicate_keys of 'a key list ]) create_optionscreate_mapped get_key get_data [x1,...,xn]
= of_alist [get_key x1, get_data x1; ...; get_key xn, get_data xn]val create_with_key : ('a key, 'r, get_key:('r ‑> 'a key) ‑> 'r list ‑> [ `Ok of ('a, 'r) t | `Duplicate_keys of 'a key list ]) create_optionscreate_with_key ~get_key [x1,...,xn]
= of_alist [get_key x1, x1; ...; get_key xn, xn]val create_with_key_or_error : ('a key, 'r, get_key:('r ‑> 'a key) ‑> 'r list ‑> ('a, 'r) t Base.Or_error.t) create_optionsval create_with_key_exn : ('a key, 'r, get_key:('r ‑> 'a key) ‑> 'r list ‑> ('a, 'r) t) create_optionsval group : ('a key, 'b, get_key:('r ‑> 'a key) ‑> get_data:('r ‑> 'b) ‑> combine:('b ‑> 'b ‑> 'b) ‑> 'r list ‑> ('a, 'b) t) create_optionsinclude Hashtbl_intf.Hashtbl_intf.Accessors with type (a, b) t := (a, b) t with type a key := a keyval sexp_of_key : ('a, _) t ‑> 'a key ‑> Base.Sexp.tval clear : (_, _) t ‑> unitAttempting to modify (set, remove, etc.) the hashtable during iteration (fold,
iter, iter_keys, iteri) will raise an exception.
val iter : (_, 'b) t ‑> f:('b ‑> unit) ‑> unitval exists : (_, 'b) t ‑> f:('b ‑> bool) ‑> boolval for_all : (_, 'b) t ‑> f:('b ‑> bool) ‑> boolval count : (_, 'b) t ‑> f:('b ‑> bool) ‑> intval length : (_, _) t ‑> intval is_empty : (_, _) t ‑> boolval partition_mapi : ('a, 'b) t ‑> f:(key:'a key ‑> data:'b ‑> [ `Fst of 'c | `Snd of 'd ]) ‑> ('a, 'c) t * ('a, 'd) tlike partition_map, but function takes both key and data as arguments
returns a pair of tables (t1, t2), where t1 contains all the elements of the
initial table which satisfy the predicate f, and t2 contains all the elements
which do not satisfy f.
find_or_add t k ~default returns the data associated with key k if it
is in the table t, otherwise it lets d = default() and adds it to the
table.
val find_and_call : ('a, 'b) t ‑> 'a key ‑> if_found:('b ‑> 'c) ‑> if_not_found:('a key ‑> 'c) ‑> 'cfind_and_call t k ~if_found ~if_not_found
is equivalent to:
match find t k with Some v -> if_found v | None -> if_not_found k
except that it doesn't allocate the option.
find_and_remove t k returns Some (the current binding) of k in t and removes
it, or None is no such binding exists
val merge : ('k, 'a) t ‑> ('k, 'b) t ‑> f:(key:'k key ‑> [ `Left of 'a | `Right of 'b | `Both of 'a * 'b ] ‑> 'c option) ‑> ('k, 'c) tMerge two hashtables.
The result of merge f h1 h2 has as keys the set of all k in the
union of the sets of keys of h1 and h2 for which d(k) is not
None, where:
d(k) =
k in h1 is to d1, and h2 does not map k;k in h2 is to d2, and h1 does not map k;k in h1 is to d1 and k in h2 is to d2.Each key k is mapped to a single piece of data x, where d(k) = Some x.
val merge_into : src:('k, 'a) t ‑> dst:('k, 'b) t ‑> f:(key:'k key ‑> 'a ‑> 'b option ‑> 'b merge_into_action) ‑> unitval filter_inplace : (_, 'b) t ‑> f:('b ‑> bool) ‑> unitval map_inplace : (_, 'b) t ‑> f:('b ‑> 'b) ‑> unitmap_inplace t ~f applies f to all elements in t, transforming them in place
val filter_map_inplace : (_, 'b) t ‑> f:('b ‑> 'b option) ‑> unitfilter_map_inplace combines the effects of map_inplace and filter_inplace
equal t1 t2 f and similar t1 t2 f both return true iff t1 and t2 have the
same keys and for all keys k, f (find_exn t1 k) (find_exn t2 k). equal and
similar only differ in their types.
val validate : name:('a key ‑> string) ‑> 'b Base.Validate.check ‑> ('a, 'b) t Base.Validate.checkinclude Hashtbl_intf.Hashtbl_intf.Multi with type (a, b) t := (a, b) t with type a key := a keyadd_multi t ~key ~data if key is present in the table then cons
data on the list, otherwise add key with a single element list.
val hashable : ('key, _) t ‑> 'key Hashable.tmodule Using_hashable : sig ... endmodule Poly : sig ... endmodule type Key_plain = Hashtbl_intf.Key_plainmodule type Key = Hashtbl_intf.Keymodule type Key_binable = Hashtbl_intf.Key_binablemodule type S_plain : Hashtbl_intf.S_plain with type ('a, 'b) hashtbl = ('a, 'b) tmodule type S : Hashtbl_intf.S with type ('a, 'b) hashtbl = ('a, 'b) tmodule type S_binable : Hashtbl_intf.S_binable with type ('a, 'b) hashtbl = ('a, 'b) tmodule Make_binable : functor (Key : Key_binable) -> S_binable with type key = Key.t