std.map.Map
type pub Map[K: Equal + Hash, V]
A hash map using linear probing and Robin Hood hashing.
A Map
preserves the order in which values are inserted, even when entries
are removed.
Performance
Preserving the insertion order makes it easier to use a Map
(e.g. when
writing tests or when serialising it), but comes with the trade-off that
removals are more expensive. Our implementation simply shifts values when
removing them. This makes removals more expensive compared to traditional maps
(O(n)
in the worst case, with n
being the number of entries), but removes
the need for using tombstones and extra indirection.
If you find yourself in a situation where you need to remove many entries from
a Map
, it may be faster to construct a new Map
that only contains the
key/value pairs you are interested in.
Size limitations
The maximum number of slots a Map
can store is 2 147 483 648, which
corresponds to a maximum of 1 932 735 283 key-value pairs (due to the load
factor being 90%). When resizing a Map
beyond this limit, a panic is
produced.
This limit shouldn't pose a problem for real-world scenarios, as most systems
won't have enough memory available to create such a Map
in the first place.
In addition, a Map
with so many values is likely the result of a bug or
better served by a more memory efficient data structure in the first place.
The reason for this limit is that Map
uses signed 32-bits indexes internally
to conserve memory, with the limit being enforced during a resize.
Algorithm
Map
uses Robin Hood hashing, with the necessary changes to make this work
while preserving insertion order. For more information on these algorithms you
can refer to the following resources:
- http://codecapsule.com/2013/11/11/robin-hood-hashing/
- http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
- https://www.sebastiansylvan.com/post/robin-hood-hashing-should-be-your-default-hash-table-implementation/
Static methods
new
Show source codeHide source code
fn pub static new -> Map[K, V] {
with_capacity(DEFAULT_CAPACITY)
}
fn pub static new -> Map[K, V]
Returns a new empty Map
.
with_capacity
Show source codeHide source code
fn pub static with_capacity(amount: Int) -> Map[K, V] {
if amount <= 0 { invalid_capacity(amount) }
let mut size = amount.nearest_power_of_two
let mut resize_at = resize_threshold(size)
# If the amount is greater than the resize threshold we'd still need a
# resize, so in this case we double the capacity (due to it always being a
# power of two).
if resize_at < amount {
size = (size * 2).nearest_power_of_two
resize_at = resize_threshold(size)
}
if size > MAX_SIZE { map_too_large }
let slots = Array.filled(with: Slot.empty, times: size)
let entries = Array.with_capacity(amount)
Map(slots: slots, entries: entries, resize_at: resize_at)
}
fn pub static with_capacity(amount: Int) -> Map[K, V]
Returns a new Map
with space for at least amount
values before resizing
the Map
is necessary.
The actual capacity may be greater due to the load factor of a Map
, but it
will never be less than amount
.
Panics
This method panics if amount
is less than or equal to zero.
Examples
let map = Map.with_capacity(32)
map.capacity # => 57
map.set('name', 'Alice')
Instance methods
!=
Show source codeHide source code
fn pub !=(other: ref Self) -> Bool {
(self == other).false?
}
fn pub !=(other: ref Self) -> Bool
if
V: Equal
Returns true
if self
and the given object are not equal to each other.
==
Show source codeHide source code
fn pub ==(other: ref Map[K, V]) -> Bool {
if size != other.size { return false }
for (k, v) in iter {
match other.entries_index(k) {
case EMPTY -> return false
case idx if other.entries.get(idx).or_panic.value != v -> return false
case _ -> {}
}
}
true
}
fn pub ==(other: ref Map[K, V]) -> Bool
if
V: Equal
Returns true
if self
and the given Map
are identical to each
other.
Examples
Comparing two Map
instances:
let map1 = Map.new
let map2 = Map.new
map1.set('name', 'Alice')
map2.set('name', 'Alice')
map1 == map2 # => true
capacity
Show source codeHide source code
fn pub capacity -> Int {
@resize_at
}
fn pub capacity -> Int
Returns the number of key-value pairs self
can store before a resize is
required.
Examples
Map.new.capacity # => 7
Map.with_capacity(8).capacity # => 14
clear
Show source codeHide source code
fn pub mut clear {
@slots = Array.filled(with: Slot.empty, times: @slots.size)
@entries.clear
}
fn pub mut clear
Removes all values in self
.
Examples
let map = Map.new
map.set('name', 'Alice')
map.set('age', '42')
map.clear
map.size # => 0
clone
Show source codeHide source code
fn pub clone -> Map[K, V] {
Map(slots: @slots.clone, entries: @entries.clone, resize_at: @resize_at)
}
fn pub clone -> Map[K, V]
if
K: Equal + Hash + Clone,
V: Clone
Creates a clone of self
.
The returned value is an owned value that is the same type as the receiver
of this method. For example, cloning a ref Array[Int]
results in a
Array[Int]
, not another ref Array[Int]
.
contains?
Show source codeHide source code
fn pub contains?(value: ref K) -> Bool {
entries_index(value) > EMPTY
}
fn pub contains?(value: ref K) -> Bool
Returns true
if self
contains the key key
.
Examples
let map = Map.new
map.set('name', 'Alice')
map.contains?('name') # => true
map.contains?('city') # => false
fmt
Show source codeHide source code
fn pub fmt(formatter: mut Formatter) {
formatter.write('{')
for (index, (k, v)) in iter.with_index {
if index > 0 { formatter.write(', ') }
k.fmt(formatter)
formatter.write(': ')
v.fmt(formatter)
}
formatter.write('}')
}
fn pub fmt(formatter: mut Formatter)
if
K: Equal + Hash + Format,
V: Format
Formats self
in a human-readable format for debugging purposes.
get
Show source codeHide source code
fn pub get(key: ref K) -> Result[ref V, MissingKey[K]] {
match entries_index(key) {
case EMPTY -> Result.Error(MissingKey.new(key))
case index -> Result.Ok(@entries.get(index).or_panic.value)
}
}
fn pub get(key: ref K) -> Result[ref V, MissingKey[K]]
Returns an immutable borrow of the key's value.
If the key is missing, a MissingKey
error is returned.
Examples
Getting the value of a missing key:
let map = Map.new
map.get('name') # => Result.Error(MissingKey(...))
Getting the value of an existing key:
let mut map = Map.new
map.set('name', 'Alice')
map.get('name') # => Result.Ok('Alice')
get_mut
Show source codeHide source code
fn pub mut get_mut(key: ref K) -> Result[mut V, MissingKey[K]] {
match entries_index(key) {
case EMPTY -> Result.Error(MissingKey.new(key))
case index -> Result.Ok(@entries.get_mut(index).or_panic.value)
}
}
fn pub mut get_mut(key: ref K) -> Result[mut V: mut, MissingKey[K]]
if
V: mut
Returns a mutable borrow of the key's value.
If the key is missing, a MissingKey
error is returned.
Examples
Getting the value of a missing key:
let map = Map.new
map.get_mut('example') # => Result.Error(MissingKey(...))
Getting the value of an existing key:
let mut map = Map.new
map.set('example', (10, 20))
map.get_mut('example') # => Result.Ok(mut (10, 20))
hash
Show source codeHide source code
fn pub hash[H: mut + Hasher](hasher: mut H) {
for (k, v) in iter {
k.hash(hasher)
v.hash(hasher)
}
}
fn pub hash[H: mut + Hasher](hasher: mut H: mut)
if
K: Equal + Hash + Hash,
V: Hash
Writes the hash for self
into the given Hasher
.
into_iter
Show source codeHide source code
fn pub move into_iter -> Stream[(K, V)] {
let iter = @entries.into_iter
Stream(fn move {
match iter.next {
case Some({ @key = k, @value = v }) -> Option.Some((k, v))
case _ -> Option.None
}
})
}
fn pub move into_iter -> Stream[(K, V)]
Returns an Iter
that iterates over all key-value pairs in this
Map
, yielding them by value.
Examples
let mut map = Map.new
map.set('name', 'Alice')
for entry in map.into_iter {
entry.key # => 'name'
entry.value # => 'Alice'
}
iter
Show source codeHide source code
fn pub iter -> Stream[(ref K, ref V)] {
let mut idx = 0
Stream(fn move {
match @entries.get(idx) {
case Ok(e) -> {
idx += 1
Option.Some((e.key, e.value))
}
case _ -> Option.None
}
})
}
fn pub iter -> Stream[(ref K, ref V)]
Returns an iterator of immutable key-value pairs.
Examples
Iterating over all the key-value pairs:
let mut map = Map.new
map.set('name', 'Alice')
for entry in map.iter {
entry.key # => 'name'
entry.value # => 'Alice'
}
iter_mut
Show source codeHide source code
fn pub mut iter_mut -> Stream[(ref K, mut V)] {
let mut idx = 0
Stream(fn move {
match @entries.get_mut(idx) {
case Ok(e) -> {
idx += 1
Option.Some((e.key, e.value))
}
case _ -> Option.None
}
})
}
fn pub mut iter_mut -> Stream[(ref K, mut V: mut)]
if
V: mut
Returns an iterator of mutable key-value pairs.
Examples
Iterating over all the key-value pairs:
let mut map = Map.new
map.set('name', 'Alice')
for entry in map.iter_mut {
entry.key # => 'name'
entry.value # => 'Alice'
}
keys
Show source codeHide source code
fn pub keys -> Stream[ref K] {
let mut idx = 0
Stream(fn move {
match @entries.get(idx) {
case Ok(e) -> {
idx += 1
Option.Some(e.key)
}
case _ -> Option.None
}
})
}
fn pub keys -> Stream[ref K]
Returns an Iter
visiting all the keys in this Map
.
Examples
Iterating over the keys in a Map
:
let mut map = Map.new
map.set('name', 'Alice')
for key in map.keys {
key # => 'name'
}
merge
Show source codeHide source code
fn pub mut merge(other: Map[K, V]) {
for (k, v) in other { set(k, v) }
}
fn pub mut merge(other: Map[K, V])
Merges two Map
objects together.
Examples
let map1 = Map.new
let map2 = Map.new
map1.set('name', 'Alice')
map2.set('city', 'Amsterdam')
map1.merge(map2)
map1['name'] # => 'Alice'
map2['city'] # => 'Amsterdam'
remove
Show source codeHide source code
fn pub mut remove(key: ref K) -> Result[V, MissingKey[K]] {
let mut slot_idx = index_for(hash_key(key))
let mut dist = 0
let mut slot = @slots.get(slot_idx).or_panic
loop {
if slot.empty? or dist > slot.distance { throw MissingKey.new(key) }
if @entries.get(slot.entry).or_panic.key == key { break }
slot_idx = index_for(slot_idx + 1)
slot = @slots.get(slot_idx).or_panic
dist += 1
}
let value = @entries.remove_at(slot.entry).or_panic.into_value
@slots.set(slot_idx, Slot.empty)
# Because we shifted the entries to the left, any slots pointing to entries
# _after_ the removed value have to be updated accordingly.
#
# We have to iterate over _all_ the slots because any slot can point to an
# entry after the one that was removed.
let mut shift_idx = 0
while shift_idx < @slots.size {
let shift = @slots.get(shift_idx).or_panic
if shift.entry > slot.entry { @slots.set(shift_idx, shift.reduce_entry) }
shift_idx += 1
}
let mut prev_slot = slot_idx
slot_idx = index_for(slot_idx + 1)
# Removing an entry means we migh be able to shift slots that were pushed to
# the right (= its distance is increased) due to a collision.
loop {
let slot = @slots.get(slot_idx).or_panic
if slot.empty? or slot.distance == 0 { break }
@slots.set(slot_idx, Slot.empty)
@slots.set(prev_slot, slot.reduce_distance)
prev_slot = slot_idx
slot_idx = index_for(slot_idx + 1)
}
Result.Ok(value)
}
fn pub mut remove(key: ref K) -> Result[V, MissingKey[K]]
Removes the given key, returning its value.
If the key is missing, a MissingKey
error is returned.
Examples
Removing a non-existing key:
let mut map = Map.new
map.remove('name') # => Result.Error(MissingKey(...))
Removing an existing key:
let mut map = Map.new
map.set('name', 'Alice')
map.remove('name') # => Result.Ok('Alice')
set
Show source codeHide source code
fn pub mut set(key: K, value: V) -> Option[V] {
if size >= @resize_at { resize }
let hash = hash_key(key)
let entry = Entry(key: key, value: value, hash: hash)
insert_entry(entry)
}
fn pub mut set(key: K, value: V) -> Option[V]
Inserts the key and value in this Map
, returning the previous value if
there is any.
Examples
Inserting a new key-value pair:
let mut map = Map.new
map.set('name', 'Alice') # => Option.Some('Alice')
size
Show source codeHide source code
fn pub size -> Int {
@entries.size
}
fn pub size -> Int
Returns the number of key-value pairs in this map.
Examples
Using an empty map:
let map = Map.new
map.size # => 0
Using a map with one key-value pair:
let map = Map.new
map.set('name', 'Alice')
map.size # => 1
values
Show source codeHide source code
fn pub values -> Stream[ref V] {
let mut idx = 0
Stream(fn move {
match @entries.get(idx) {
case Ok(e) -> {
idx += 1
Option.Some(e.value)
}
case _ -> Option.None
}
})
}
fn pub values -> Stream[ref V]
Returns an iterator that yields immutable references to the values in
self
.
Examples
Iterating over the values in a Map
:
let mut map = Map.new
map.set('name', 'Alice')
for value in map.values {
value # => 'Alice'
}
values_mut
Show source codeHide source code
fn pub mut values_mut -> Stream[mut V] {
let mut idx = 0
Stream(fn move {
match @entries.get_mut(idx) {
case Ok(e) -> {
idx += 1
Option.Some(e.value)
}
case _ -> Option.None
}
})
}
fn pub mut values_mut -> Stream[mut V: mut]
if
V: mut
Returns an iterator that yields mutable references to the values in self
.
Examples
Iterating over the values in a Map
:
let mut map = Map.new
map.set('name', 'Alice')
for value in map.values_mut {
value # => 'Alice'
}
Implemented traits
Clone
impl Clone for Map
if
K: Equal + Hash + Clone,
V: Clone
Equal
impl Equal for Map
if
V: Equal
Format
impl Format for Map
if
K: Equal + Hash + Format,
V: Format
Hash
impl Hash for Map
if
K: Equal + Hash + Hash,
V: Hash