Maps¶
template<typename K, typename V, typename... Properties>
using Map = Store<map<K, V>, Properties...>; // written out as Store<map<K, V>, ...> in code
-
class Map¶
A map:
Store<map<K, V>, Properties...>. The nameMapstands for the resolved Store type on this page.
A map holds one value per key, or many (multi: a multimap). Its elements are
std::pair<const K, V>; a dense map iterates each key with a view of its value.
Request |
Representation |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Template parameters¶
KAs for a set:
hashable,orderablewithsorted,indexablewithdense.VAny type. A Store as a value is a row: see Rows: Stores as values and elements.
Member types¶
|
|
|
|
|
|
|
the representation’s; a dense map’s reference is a pair of the key and a view of its value |
Member functions¶
Construction, capacity, iterators, modifiers by element (insert, emplace, erase, clear,
erase_if), layers and comparison are as for Sets, over pairs. erase(key) takes a key, and so do the
lookups:
Lookup¶
-
const_iterator find(const key_type &key) const¶
-
size_type count(const key_type &key) const¶
-
bool contains(const key_type &key) const¶
-
const_iterator lower_bound(const key_type &key) const¶
-
const_iterator upper_bound(const key_type &key) const¶
-
std::pair<const_iterator, const_iterator> equal_range(const key_type &key) const¶
The bounds with
sorted, unless the map is ordered by its values, which is searched by key only.
Values by key (one value per key)¶
-
const mapped_type &at(const key_type &key) const¶
-
mapped_type &at(const key_type &key)¶
The value;
std::out_of_rangewhere the key has none. The mutable one is not offered on afrozenmap or one ordered by its values.
-
mapped_type &operator[](const key_type &key)¶
The value for
key, inserted as a default first where there is none (not frozen, default-constructible value).
-
template<typename ...Args>
std::pair<iterator, bool> try_emplace(const key_type &key, Args&&... args)¶
-
template<typename Update>
decltype(auto) update(const key_type &key, Update update)¶ Calls
update(value, held)on the value ofkey, inserting a default one first where there is none (heldis thenfalse), and returns whatupdatereturns. A map cleared by generation hands over the value it kept from before the clear, storage and all, forupdateto reuse. Withconcurrent, threads may update at once: a sharded table runsupdateunder the key’s lock. Only where the representation has it (maps by index, sharded tables, layers); any other map is updated throughtry_emplace.
-
template<typename F, typename Result>
Result visit(const key_type &key, F f, Result missing) const¶ f(value)wherekeyhas a value, elsemissing(the visit of Boost’s concurrent maps). Withconcurrent, safe while others update. Only where the representation has it, asupdate; any other map is read throughfind.
Values by key (a multimap)¶
The entries of a key are its equal_range (above), as std::multimap’s: its values are
std::ranges::subrange(first, last) | std::views::values.
Entries in order (insertion_ordered)¶
-
auto keys() const¶
-
auto values() const¶
The keys and the values as spans, in the order the keys first came.
-
std::size_t position(const_iterator it) const¶
Inverse (bidirectional)¶
-
auto keys(const mapped_type &value) const¶
A range of the keys that map to
value: a frozen graph’s in-edges.
Scopes (scoped)¶
-
void push_scope()¶
-
void pop_scope()¶
Opens a scope; undoes every change made since the matching
push_scope, latest first, so an inner binding shadows an outer one until its scope ends. Two scoped maps compare equal by the bindings in force, whatever scopes are open.
-
std::size_t depth() const¶
How many scopes are open.
clearcloses every scope.
-
bool bound_in_scope(const key_type &key) const¶
Whether
keyis bound and the innermost open scope bound it; with no scope open, whether it is bound at all. A redeclaration check:bound_in_scope(name)before declaringnamein the current block.
Layers (layered)¶
A layer is over a map by index (dense): a repository’s per-predicate directory over a parent’s.
-
void layer_over(const Map *parent)¶
Lookups find
parent’s values too, from now on;nullptrends it. The Store holds, iterates and erases only its own values; inserting a key the parent holds inserts nothing. The parent is not owned: it must outlive the layer and stay unchanged. A layered Store has no==: its contents include its parent’s.
-
const Map &owner(const key_type &key) const¶
The Store along the chain whose own values hold
key: this one, or an ancestor. Some layer must hold it (contains(key)).
A key’s own value shadows the parent’s. insert_or_assign writes its own; at(), update() and
try_emplace(key, args...) on a layer that may change make an own value where the layer has none, once: where
values layer themselves (a map of layered pools layers through both levels, see Records, repositories, views), one built from
try_emplace’s arguments (a pool’s urd::bit_width or urd::column_widths), or default, layered over the parent’s value; else one built
from the arguments, or else a copy of the parent’s. Read through a const Store (at, read) to make nothing.
With concurrent, at, update, read, try_emplace, contains and owner run from threads at
once: each own value is made and layered once, under its slot’s lock, and threads then insert into it as the value
allows (a concurrent pool). clear, erase, insert_or_assign and iteration need no other operation
running.
Complexity¶
Representation |
ordered lookup |
scope |
iterate |
lookup |
insert |
erase |
clear |
memory |
holds |
|---|---|---|---|---|---|---|---|---|---|
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(1) |
O(U) |
O(U), 2 words |
a slot per index |
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(1) |
O(U) |
O(U), 2.1 words |
a slot per index, in segments that never move |
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(1) |
O(1) |
O(U), 3.3 words |
a slot and a generation per index |
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(1) |
O(1) |
O(U), 4.1 words |
a slot and a generation per index, in segments that never move |
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(1) |
O(1) |
O(U), 4.1 words |
a slot, a generation and a lock byte per index, in segments |
|
— |
— |
O(n) |
as its part |
as its part |
O(n) |
as its part |
O(U), 3.7 words |
its sparse set + n values in order |
|
as its part |
O(changes) |
as its part |
as its part |
as its part |
as its part |
as its part |
O(U), 2.2 words |
|
|
— |
— |
as its part |
as its part |
as its part |
as its part |
as its part |
O(U), 2 words |
its own elements; the parent is not owned |
|
— |
— |
as its part |
as its part |
as its part |
as its part |
as its part |
O(U), 3.3 words |
its own elements; the parent is not owned |
|
— |
— |
as its part |
as its part |
as its part |
as its part |
as its part |
O(U), 4.1 words |
its own elements; the parent is not owned |
|
— |
— |
O(capacity) |
O(1)~ |
O(1)*~ |
O(1)~ |
O(capacity) |
O(capacity), 2.4 words |
~n/0.875 slots + 1 byte each; retains |
|
— |
— |
O(n) |
O(1)~ |
O(1)*~ |
O(1)~ |
O(n) |
O(n), 5 words |
a node per entry + buckets; stable references |
|
— |
— |
O(capacity) |
O(1)~ |
O(1)*~ |
O(1)~ |
O(capacity) |
O(capacity), 2.1 words |
16 shards of open addressing, a lock each |
|
— |
— |
O(capacity) |
O(1)~ |
O(1)*~ |
O(1)~ |
O(capacity) |
O(n), 3 words |
16 shards, a node per entry; stable references |
|
O(log n) |
— |
O(n) |
O(log n) |
O(log n) |
O(log n) |
O(n) |
O(n), 5 words |
a node per entry; reclaims |
|
O(log n) |
— |
O(n) |
O(log n) |
O(n) |
O(n) |
O(n) |
O(n), 1.5 words |
n entries in one array; retains |
|
— |
— |
as its part |
as its part |
as its part |
as its part |
O(n) |
O(n), 4.5 words |
erased nodes and their values kept for reuse |
|
as its part |
— |
as its part |
as its part |
as its part |
as its part |
O(n) |
O(n), 5.5 words |
erased nodes and their values kept for reuse |
|
— |
— |
O(n) |
as its part |
as its part |
O(n) |
as its part |
O(capacity), 3.5 words |
its key set + n values in order |
|
— |
O(changes) |
as its part |
as its part |
as its part |
as its part |
as its part |
O(capacity), 2.3 words |
|
|
as its part |
O(changes log changes) |
as its part |
as its part |
as its part |
as its part |
as its part |
O(n), 6 words |
|
|
— |
— |
O(n) |
O(1)~ |
O(1)*~ |
O(r) |
O(capacity) |
O(capacity), 9 words |
a vector of values per key |
|
— |
— |
O(n) |
O(1)~ |
O(1)*~ |
O(r) |
O(capacity) |
O(capacity), 8.5 words |
a slot per value, freed slots reused by any key; retains |
|
— |
— |
O(n) |
O(1)~ |
O(1)*~ |
O(r)~ |
O(n) |
O(n), 4.8 words |
a node per entry + buckets; stable references |
|
O(log n) |
— |
O(n) |
O(log n) |
O(log n) |
O(log n) |
O(n) |
O(n), 5 words |
a node per entry; reclaims |
|
O(log n) |
— |
O(n) |
O(log n) |
O(n) |
O(n) |
O(n) |
O(n), 1.5 words |
n entries in one array; retains |
|
O(U) |
— |
O(U) |
O(1) |
O(1)* |
O(r) |
O(U) |
O(U), 6.2 words |
a vector per index; retains |
|
O(U) |
— |
O(U) |
O(1) |
— |
— |
— |
O(U), 2.4 words |
n values + U + 1 offsets |
|
O(U) |
— |
O(U) |
O(1) |
— |
— |
— |
O(U), 4.8 words |
n values + U + 1 offsets, both ways |
* amortized, ~ expected; n elements, U the key range, r a row’s length, k a delta’s changes. Memory is the heap it holds (urd::memory_usage): what it grows in, and per element (or key, or slot) at most so many words, each the size of an element and at least a machine word. The declared costs; what they measure to is in Benchmarks.
Rows: Stores as values and elements¶
A Store of Stores is written as one: an element, key or value can be another Store, to any depth. Most rows are
held as Stores, each owning its storage. Where the outer request says how rows live, urd stores them together and
hands out a row as a view (Rows::reference names what a row is handed out as):
Rows |
Request |
Container |
A row is |
|---|---|---|---|
never change |
|
|
|
fixed lengths, changing contents |
|
|
|
… of dense indices |
|
|
a |
reused after removal |
|
|
the row Store, cleared, not freed |
map values reused after erase |
|
|
the value Store, cleared, kept with its node |
interned, by handle |
|
|
the row, read by handle (Pools) |