Search results

There are no results.

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:

Static methods

new

Show source code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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 code
Hide 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

std.clone.

Clone

impl Clone for Map
if
  K: Equal + Hash + Clone,
  V: Clone
std.cmp.

Equal

impl Equal for Map
if
  V: Equal
std.fmt.

Format

impl Format for Map
if
  K: Equal + Hash + Format,
  V: Format
std.hash.

Hash

impl Hash for Map
if
  K: Equal + Hash + Hash,
  V: Hash