A HashMap implementation based on a Hash Array Mapped Trie
The hash array mapped trie achieves almost hash table-like speed while using memory much more economically. Also, a hash table may have to be periodically resized, an expensive operation, whereas HAMTs grow and shrink dynamically.
Get an empty map with Monoid.mempty or ListEmpty.empty, make a singleton one with singleton or turn an association list into a HashMap with fromList. The more general function fromListWith allows custom handling of associations with duplicate keys.
Use insert, delete, adjust and replace. The more general form of insert is insertWith which accepts a function to combine the given value with an already existing one.
The basic function is lookup, of which member and lookupDefault are variants. The operator (!!) may be used when the existence of the keys looked for is out of the question.
There is union, difference and intersection. More general functions unionWith and intersectionWith allow combination of the affected values.
Left folds as well as right folds are provided by foldValues and foldrValues. Variants foldWithKey and foldrWithKey allow examination not only of the value, but also of the key.
Frequently needed functions such as values, keys, each and size are just predefined folds for your convenience.
Create a subset of an existing map with filterValues or filterWithKey.
mapValues, mapWithKey and traverseWithKey should cover any need to transform an existing map.
Functions whose name have the With suffix take a custom function to combine two values and are thus more general than the ones without that suffix. Most often it is the case that
xxx = xxxWith const
Functions whose name have the Values suffix operate on the values of the mappings contained in the map and take an appropriate custom function as argument. The Values suffix also serves to avoid conflicts with Prelude functions (i.e. map, filter, Foldable.fold, Foldable.foldr).
The companions of the Values functions have the suffix WithKey and accept functions that take an extra argument for the key. The key portion of a mapping or association is always passed first, followed by the associated value.
O(1) Create a singleton map
O(n) Compute the size of the map
O(n) Retrieve a list of the values in the map
O(n) Retrieve a list of the keys in the map
O(n) Retrieve a list of the associations in the map
O(log n)
insert k v m
is a HashMap h such that
lookup k h = Just v
and lookup for any other key o
lookup o h = lookup o m
Less formally said, k is associated with v in the resulting map, updating a previously existing association of k if it exists, while all other associations are left untouched.
In the case of an update, the new value will get forced, see insertWith for details.
O(log n)
insertWith f k v m
If m does not contain k, this works like insert. Otherwise, the existing association of k with some value v' is replaced by an association of k with the result of evaluating
f v v'
in the resulting map.
Strict evaluation is necessary to prevent building up of large thunks of the form
f v3 (f v2 (f v1 v0))
Note that
insert = insertWith const
and that this will evaluate the new value in case of an update. If you want to prevent this, use
replace k v = insert k v . delete k
The replaced value will be evaluated only if the given function is strict in the second argument. Since const is lazy in the second argument, the following will be fine:
insert "foo" 7 (insert "foo" undefined (delete "foo" m))
That is, the value that is inserted for a given key first is not evaluated on insertion, and only evaluated on update if the update function demands it, which is not the case for a plain insert.
O(log n)
delete k m
is a HashMap h such that
lookup k h = Nothing
and for any other key o
lookup o h = lookup o m
Less formally, the association of k with some value, if any, is removed in the result, while all other associations are retained.
If m didn't contain k in the first place,
delete k m = m
O(log n)
lookup k m
If k is associated with some value v in map m, it returns
Just v
and otherwise
Nothing
O(log n)
Checks whether the key is present in the map
O(log n)
Return the value to which the specified key is mapped, or the default value if this map contains no mapping for the key.
O(log n)
Return the value associated with the given key in the map. Fails with error if the key is not present.
O(log n)
Adjust the value tied to a given key in this map only if it is present. Otherwise, leave the map alone.
O(log n)
replace k v m = insert k v . delete k $ m
Insert or update the association of k with v in m but avoid evaluation of v even if m already contains k.
See also notes concerning updates on function insertWith.
O(m*log n)
Computes the union of two hash maps.
If a key occurs in both maps, the function provided in the first argument will be used to compute the result in the same way as insertWith would do it, that is, the value from the left hash map will be evaluated while the value from the right map may be evaluated only if the function demands it. However, values associated with keys that are member of only one map are left alone.
O(m*log n)
Computes the union of two hash maps.
If a key occurs in both maps, the value from the left map will be evaluated and taken over to the new map.
Because
union = unionWith const
the considerations concerning strictness apply for union in the same way as for unionWith.
The union of all HashMaps in a list.
O(n)
Reduce this map by applying a function to all associations, using the given starting value (typically the left-identity of the operator). Each application of the function is evaluated before using the result in the next application.
This function is strict in the starting value.
O(n)
Reduce this map by applying a binary operator to all values, using the given starting value (typically the left-identity of the operator). Each application of the operator is evaluated before using the result in the next application.
This function is strict in the starting value.
O(n)
Reduce this map by applying a binary operator to all values, using the given starting value (typically the right-identity of the operator).
Warning: this function exists for Haskell compatibility only. Please be aware that right folds suffer from the danger of stack overflows, while left folds don't and are also faster because of tail recursion. Since the order of values is arbitrary anyway, there is often no good reason to insist on a right fold, so please use foldValues instead.
O(n)
Reduce this map by applying a binary operator to all mappings, using the given starting value (typically the right-identity of the operator).
Warning: this function exists for Haskell compatibility only. Please be aware that right folds suffer from the danger of stack overflows, while left folds don't and are also faster because of tail recursion. Since the order of values is arbitrary anyway, there is often no good reason to insist on a right fold, so please use foldWithKey instead.
O(n)
Transform a map by applying a function to every value.
O(n)
Transform a map by applying a function to every key and its associated value.
O(n)
Transform a map by applying an applicative functor to every key and its associated value.
O(n)
Filter a map, retaining only mappings whose key and value satisfy a given predicate.
O(n)
Filter a map, retaining only mappings whose value satisfies a given predicate.
O(n*log m)
Computes the difference of two maps.
Returns a map that contains the mappings of the first map whose keys do not exist in the second.
O(n*log m)
Computes the intersection of two maps.
Return a map that contains the mappings of the first map for keys that also exist in the second.
O(n*log m)
Computes the intersection of two maps, combining the values with a given function.
O(n)
Build a map from an association list. If the list contains duplicate mappings, the later mappings take precedence.
O(n)
Build a map from an association list. Uses the provided function to merge values associated with duplicate keys.
A map from hashable keys to values based on a Hash Mapped Array Trie.
A map cannot contain duplicate keys; each key can map to at most one value. A HashMap makes no guarantees as to the order of its elements.
A node of the HashMap is either
This implementation of a persistent hash array mapped trie uses 32 bit hash values as provided by Java and the Frege Eq type class.
To find a value, the search starts with the root node. If the node is a key/value pair, the node's key is compared to the search key. When the keys are equal, the value is returned, otherwise the key is not in the map.
If the node is a bitmapped node, the hash code of the lookup key is computed and the presence of the index provided by the last five bits is checked in the bitmap. If it is there, the search continues with the corresponding node from the node array, otherwise the key is not in the map. With every recursion, the next five bits of the hash code will be used for indexing.
It remains the case that the node is a collision list. The searched key's hashcode either is the same as the one of the keys in the collision list, in which case the search degrades to a sequential search in that list, or it is different, and in the latter case we know that the key is not in the map without even touching the list.
Hence, the worst case in searching must do the following:
It turns out that - absent hash collisions - lookups will be done almost in constant time. And so will be inserts and deletes, although with a slightly larger constant factor due to the house-keeping necessary for a persistent data structure. However, even this are in the worst case 7 array copies, where 6 of them may be of size 32 and one of size 4. Assuming that the pointers are 4 bytes long, this amounts to copying at most 196*4 bytes of memory.
The map can have at most 2^32 non-bitmapped nodes maintained in 1+32+1024+32768+1048576+33554432+1073741824 bitmapped nodes. But because collision lists can be arbitrary long, the total number of key/value pairs is not limited.
Bitmapped node. It has a bitmap of 32 bits that indicate presence or absence of a sub node for a given index which is in the range [0..31], and an array of sub nodes. The size of the array is equal to the number of 1-bits in the bitmap. An index is mapped to an actual array index like so: If the corresponding Bits.bit is set in the bitmap, the number of less significant 1-bits in the bitmap is counted with Bits.bitCount and this is then the index in the array. Otherwise there is no sub node for that index.
Collision node, holding a list of key/value tuples as well as the Eq.hashCode all keys have in common. This helps us avoid touching the list when the sought key has a different hash code.
Singleton node holding a key with a value. Also caches the Eq.hashCode of the key to avoid possibly expensive recomputation.
access field bitmap
O(n) Compute a 3-tuple of
O(log n)
hm.delete k
Variant of delete that is better suited for left folds and supports dot-notation.
access field hash
transform an index into an actual array index
indexMap bmap nodes inx
bmap is the bitmap
nodes is the number of actual subnodes
inx is a hash code or part of a hash code, whose least significant 5 bits are the index
returns a number in the range 0..nodes, where nodes means "no corresponding node"
O(log n)
hm.insert k v
Variant of insert that is better suited for left folds and supports dot-notation.
this checks the invariants for a node
access field key
access field list
O(log n)
hm.lookup k
Variant of lookup that supports dot-notation.
access field subnodes
access field value
inherited from ArrayElement.arrayFromIndexList
inherited from ArrayElement.arrayFromIndexListST
inherited from ArrayElement.arrayFromList
inherited from ArrayElement.arrayFromListST
inherited from ArrayElement.arrayFromMaybeList
inherited from ArrayElement.arrayFromMaybeListST
inherited from ArrayElement.arrayLength
inherited from ArrayElement.elemAt
inherited from ArrayElement.getAt
inherited from ArrayElement.getElemAt
inherited from ArrayElement.itemAt
inherited from ArrayElement.listFromArray
inherited from ArrayElement.maybeListFromArray
inherited from ArrayElement.modifyAt
inherited from ArrayElement.modifyElemAt
inherited from ArrayElement.newArray
inherited from ArrayElement.setAt
inherited from ArrayElement.setElemAt
inherited from Eq.!=
inherited from Foldable.fold
inherited from Foldable.fold1
inherited from Foldable.foldMap
inherited from Foldable.foldMap1
inherited from Foldable.foldl1
inherited from Foldable.foldr1
inherited from ListMonoid.concat
inherited from Monoid.mconcat
The empty HashMap.
inherited from Monoid.mtimes
inherited from Semigroup.sconcat
inherited from Semigroup.stimes
inherited from Show.display
inherited from Show.showChars
inherited from Show.showList
inherited from Show.showsPrec
inherited from Show.showsub
inherited from Traversable.mapM
inherited from Traversable.sequence
inherited from Traversable.sequenceA
ListEmpty_HashMap.null, HashMap.has$subnodes, HashMap.has$key, HashMap.has$list, HashMap.has$value, HashMap.has$bitmap, HashMap.has$hash
Show_HashMap.showsub, Show_HashMap.display, Show_HashMap.show
ArrayElement_HashMap.modifyElemAt, ArrayElement_HashMap.modifyAt