mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-09 23:23:59 +00:00
204 lines
7.7 KiB
Plaintext
204 lines
7.7 KiB
Plaintext
[#regular]
|
|
= Regular Containers
|
|
|
|
:idprefix: regular_
|
|
|
|
Boost.Unordered closed-addressing containers (`boost::unordered_set`, `boost::unordered_map`,
|
|
`boost::unordered_multiset` and `boost::unordered_multimap`) are fully conformant with the
|
|
C++ specification for unordered associative containers, so for those who know how to use
|
|
`std::unordered_set`, `std::unordered_map`, etc., their homonyms in Boost.Unordered are
|
|
drop-in replacements. The interface of open-addressing containers (`boost::unordered_node_set`,
|
|
`boost::unordered_node_map`, `boost::unordered_flat_set` and `boost::unordered_flat_map`)
|
|
is very similar, but they present some minor differences listed in the dedicated
|
|
xref:compliance.adoc#compliance_open_addressing_containers[standard compliance section].
|
|
|
|
|
|
For readers without previous experience with hash containers but familiar
|
|
with normal associative containers (`std::set`, `std::map`,
|
|
`std::multiset` and `std::multimap`), Boost.Unordered containers are used in a similar manner:
|
|
|
|
[source,cpp]
|
|
----
|
|
typedef boost::unordered_map<std::string, int> map;
|
|
map x;
|
|
x["one"] = 1;
|
|
x["two"] = 2;
|
|
x["three"] = 3;
|
|
|
|
assert(x.at("one") == 1);
|
|
assert(x.find("missing") == x.end());
|
|
----
|
|
|
|
But since the elements aren't ordered, the output of:
|
|
|
|
[source,c++]
|
|
----
|
|
for(const map::value_type& i: x) {
|
|
std::cout<<i.first<<","<<i.second<<"\n";
|
|
}
|
|
----
|
|
|
|
can be in any order. For example, it might be:
|
|
|
|
[source]
|
|
----
|
|
two,2
|
|
one,1
|
|
three,3
|
|
----
|
|
|
|
There are other differences, which are listed in the
|
|
<<comparison,Comparison with Associative Containers>> section.
|
|
|
|
== Iterator Invalidation
|
|
|
|
It is not specified how member functions other than `rehash` and `reserve` affect
|
|
the bucket count, although `insert` can only invalidate iterators
|
|
when the insertion causes the container's load to be greater than the maximum allowed.
|
|
For most implementations this means that `insert` will only
|
|
change the number of buckets when this happens. Iterators can be
|
|
invalidated by calls to `insert`, `rehash` and `reserve`.
|
|
|
|
As for pointers and references,
|
|
they are never invalidated for node-based containers
|
|
(`boost::unordered_[multi]set`, `boost::unordered_[multi]map`, `boost::unordered_node_set`, `boost::unordered_node_map`),
|
|
but they will be when rehashing occurs for
|
|
`boost::unordered_flat_set` and `boost::unordered_flat_map`: this is because
|
|
these containers store elements directly into their holding buckets, so
|
|
when allocating a new bucket array the elements must be transferred by means of move construction.
|
|
|
|
In a similar manner to using `reserve` for ``vector``s, it can be a good idea
|
|
to call `reserve` before inserting a large number of elements. This will get
|
|
the expensive rehashing out of the way and let you store iterators, safe in
|
|
the knowledge that they won't be invalidated. If you are inserting `n`
|
|
elements into container `x`, you could first call:
|
|
|
|
```
|
|
x.reserve(n);
|
|
```
|
|
|
|
Note:: `reserve(n)` reserves space for at least `n` elements, allocating enough buckets
|
|
so as to not exceed the maximum load factor.
|
|
+
|
|
Because the maximum load factor is defined as the number of elements divided by the total
|
|
number of available buckets, this function is logically equivalent to:
|
|
+
|
|
```
|
|
x.rehash(std::ceil(n / x.max_load_factor()))
|
|
```
|
|
+
|
|
See the xref:reference/unordered_map.adoc#unordered_map_rehash[reference for more details] on the `rehash` function.
|
|
|
|
[#comparison]
|
|
|
|
:idprefix: comparison_
|
|
|
|
== Comparison with Associative Containers
|
|
|
|
[caption=, title='Table {counter:table-counter} Interface differences']
|
|
[cols="1,1", frame=all, grid=rows]
|
|
|===
|
|
|Associative Containers |Unordered Associative Containers
|
|
|
|
|Parameterized by an ordering relation `Compare`
|
|
|Parameterized by a function object `Hash` and an equivalence relation `Pred`
|
|
|
|
|Keys can be compared using `key_compare` which is accessed by member function `key_comp()`, values can be compared using `value_compare` which is accessed by member function `value_comp()`.
|
|
|Keys can be hashed using `hasher` which is accessed by member function `hash_function()`, and checked for equality using `key_equal` which is accessed by member function `key_eq()`. There is no function object for compared or hashing values.
|
|
|
|
|Constructors have optional extra parameters for the comparison object.
|
|
|Constructors have optional extra parameters for the initial minimum number of buckets, a hash function and an equality object.
|
|
|
|
|Keys `k1`, `k2` are considered equivalent if `!Compare(k1, k2) && !Compare(k2, k1)`.
|
|
|Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`
|
|
|
|
|Member function `lower_bound(k)` and `upper_bound(k)`
|
|
|No equivalent. Since the elements aren't ordered `lower_bound` and `upper_bound` would be meaningless.
|
|
|
|
|`equal_range(k)` returns an empty range at the position that `k` would be inserted if `k` isn't present in the container.
|
|
|`equal_range(k)` returns a range at the end of the container if `k` isn't present in the container. It can't return a positioned range as `k` could be inserted into multiple place. +
|
|
**Closed-addressing containers:** To find out the bucket that `k` would be inserted into use `bucket(k)`. But remember that an insert can cause the container to rehash - meaning that the element can be inserted into a different bucket.
|
|
|
|
|`iterator`, `const_iterator` are of the bidirectional category.
|
|
|`iterator`, `const_iterator` are of at least the forward category.
|
|
|
|
|Iterators, pointers and references to the container's elements are never invalidated.
|
|
|<<regular_iterator_invalidation,Iterators can be invalidated by calls to insert or rehash>>. +
|
|
**Node-based containers:** Pointers and references to the container's elements are never invalidated. +
|
|
**Flat containers:** Pointers and references to the container's elements are invalidated when rehashing occurs.
|
|
|
|
|Iterators iterate through the container in the order defined by the comparison object.
|
|
|Iterators iterate through the container in an arbitrary order, that can change as elements are inserted, although equivalent elements are always adjacent.
|
|
|
|
|No equivalent
|
|
|**Closed-addressing containers:** Local iterators can be used to iterate through individual buckets. (The order of local iterators and iterators aren't required to have any correspondence.)
|
|
|
|
|Can be compared using the `==`, `!=`, `<`, `\<=`, `>`, `>=` operators.
|
|
|Can be compared using the `==` and `!=` operators.
|
|
|
|
|
|
|
|When inserting with a hint, implementations are permitted to ignore the hint.
|
|
|
|
|===
|
|
|
|
---
|
|
|
|
[caption=, title='Table {counter:table-counter} Complexity Guarantees']
|
|
[cols="1,1,1", frame=all, grid=rows]
|
|
|===
|
|
|Operation |Associative Containers |Unordered Associative Containers
|
|
|
|
|Construction of empty container
|
|
|constant
|
|
|O(_n_) where _n_ is the minimum number of buckets.
|
|
|
|
|Construction of container from a range of _N_ elements
|
|
|O(_N log N_), O(_N_) if the range is sorted with `value_comp()`
|
|
|Average case O(_N_), worst case O(_N^2^_)
|
|
|
|
|Insert a single element
|
|
|logarithmic
|
|
|Average case constant, worst case linear
|
|
|
|
|Insert a single element with a hint
|
|
|Amortized constant if `t` elements inserted right after hint, logarithmic otherwise
|
|
|Average case constant, worst case linear (ie. the same as a normal insert).
|
|
|
|
|Inserting a range of _N_ elements
|
|
|_N_ log(`size()` + _N_)
|
|
|Average case O(_N_), worst case O(_N_ * `size()`)
|
|
|
|
|Erase by key, `k`
|
|
|O(log(`size()`) + `count(k)`)
|
|
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
|
|
|
|Erase a single element by iterator
|
|
|Amortized constant
|
|
|Average case: O(1), Worst case: O(`size()`)
|
|
|
|
|Erase a range of _N_ elements
|
|
|O(log(`size()`) + _N_)
|
|
|Average case: O(_N_), Worst case: O(`size()`)
|
|
|
|
|Clearing the container
|
|
|O(`size()`)
|
|
|O(`size()`)
|
|
|
|
|Find
|
|
|logarithmic
|
|
|Average case: O(1), Worst case: O(`size()`)
|
|
|
|
|Count
|
|
|O(log(`size()`) + `count(k)`)
|
|
|Average case: O(1), Worst case: O(`size()`)
|
|
|
|
|`equal_range(k)`
|
|
|logarithmic
|
|
|Average case: O(`count(k)`), Worst case: O(`size()`)
|
|
|
|
|`lower_bound`,`upper_bound`
|
|
|logarithmic
|
|
|n/a
|
|
|
|
|===
|