Regular Containers

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 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:

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:

for(const map::value_type& i: x) {
    std::cout<<i.first<<","<<i.second<<"\n";
}

can be in any order. For example, it might be:

two,2
one,1
three,3

There are other differences, which are listed in the 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 vectors, 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 reference for more details on the rehash function.

Comparison with Associative Containers

Table 1 Interface differences
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.

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.


Table 2 Complexity Guarantees
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(N2)

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