License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Frédéric Pennerath, CentraleSupelec
Last modified : April 19, 2024 10:22
Link to the source : containers.md

Table of contents

Containers

The container library : a global view

Containers

The concept of Container

A Container of type T is a finite collection of elements of type T that are enumerable through iterators.

The container concept


Concept of Container
Method Meaning
size_t size() const Returns the number of elements stored.
iterator begin() Returns an iterator that can be dereferenced to a lvalue reference T&
on the first element (i.e. through T& operator*()).
const_iterator begin() const Returns a const iterator that can be dereferenced to a const rvalue reference const T&
on the first element (i.e. through const T& operator*() const).
iterator end()
const_iterator end() const
Returns an iterator pointing to the virtual position
past the last element (i.e. through operator++()).

* Iterators are a generalization of C pointers to any data structure. They adopt the same syntax of C pointers :

Concept of forward iterator
Method Meaning
iterator operator=(const iterator&) Assignment operator.
T& operator*() Returns a reference to the pointed element.
iterator& operator++() “Jumps” to the next element.
bool operator!=(const iterator& other) const Returns true if positions pointed by the two iterators are different.


* Iterators are used to enumerate elements of a container, using idiomatic iterator loops:

template<typename Container> 
void fill(Container& C, typename Container::const_reference V) {
    // Always use this idiomatic syntax for generic loops
    // Do not use operators <, > or post-increment if not necessary
    for(typename C::iterator it = C.begin(); it != C.end(); ++it) 
        *it = V;
}

template<typename Container> 
typename Container::value_type sum(const Container& C) {
    typename Container::value_type S{}; // 0 for numeric types
    for(typename C::const_iterator it = C.begin(); it != C.end(); ++it)
        S += *it;
    return S;
}
template<typename Container> 
void fill(Container& C, typename Container::const_reference V) {
    for(auto& elt : C)  elt = V;
}

template<typename Container> 
auto sum(const Container& C) {
    decltype(*C.begin() + *C.begin()) S{}; // This is a trick to avoid using type alias value_type
    for(const auto& elt : C) S += elt;
    return S;
}

Sequential containers

The concept SequenceContainer

A SequenceContainer of type T is a Container of type T whose elements are organized in a sequence within which they are identified by a unique index from 0 to size() - 1.

Sequence


Concept of SequenceContainer
Method Meaning
size_t size() const Returns the number of elements stored.
T& operator[](size_t i)
const T& operator[](size_t i) const
Returns a lvalue reference / const rvalue reference to the element of given index i
without bound checking.
T& at(size_t i)
const T& at(size_t i) const
Returns a lvalue reference / const rvalue reference to the element of given index i
after bound checking (throw std::out_of_range if out of bounds).
void push_back(const T&) Append a new element at the end.
T& back() Get a lvalue reference on the last stored element.
void push_front(const T&) Insert a new element at the first position.
T& front() Get a lvalue reference on the first stored element.

Contiguous sequential containers

A ContiguousContainer of type T is a container whose elements of type T are stored contiguously in memory (like an array).

Remarks:

Static arrays

Static arrays

Vectors (dynamic arrays)

Vectors

Strings

Strings

Spans (C++20) and string views (C++17)

Spans

String view

Non-contiguous sequential containers

Deque

Spans

Double linked list

Lists

Single linked list

Forward lists

Complexities summary

Design principle: methods, when impossible or with a suboptimal complexity are not implemented.


Complexity of operations for SequenceContainer models
Method Array Vector Deque List Forward list
size() \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\)
operator[] / at() \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\)
push_back() \(\Theta(1) / \Theta(n)^*\) \(\Theta(1) / \Theta(n)^*\) \(\Theta(1)\)
back() \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\)
push_front() \(\Theta(1) / \Theta(n)^*\) \(\Theta(1)\) \(\Theta(1)\)
front() \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\) \(\Theta(1)\)
erase() \(\Theta(n-i)\) \(\min(\Theta(i), \Theta(n-i))\) \(\Theta(1)\) \(\Theta(1)\)

Associative containers

Definition

Associative container

An associative container (also called dictionary or map) contains pairs std::pair<K,V> of key/value (key type K, value type V) such that a value can quickly be retrieved from its key, i.e in sublinear complexity, either in \(\Theta(\log(n))\) or amortized \(\Theta(1)\).

Remarks:


Concept of associative container
Method Meaning
size_t size() const Returns the number of pairs K/V stored.
V& operator[](const K& key) Returns a lvalue reference to the value mapped to the given key key after mapping key to a default value if no entry exists.
const V& at(const K& key) Returns a const rvalue reference to the value mapped to the given key key or throws a std::out_of_range exception if no entry exists.
iterator find(const K& key) Returns an iterator pointing to the pair of given key key (returns sentinel end() if no entry exists).
std::pair<iterator,bool> insert(const std::pair<K,V>& pair) Inserts, if not already existing, a pair into the map and returns its position along with an insertion boolean flag.

Ordered maps and sets

An ordered associative container is an associative container where pairs std::pair<K,V> are (implicitely) sorted in increasing order according to some total ordering relation \(\leq\) on keys K:

  • Reflexive: \(\forall k \in K, k \leq k\)
  • Transitive: \(\forall (k_1,k_2,k_3) \in K^3, k_1 \leq k_2 \text{ and } k_2 \leq k_3 \Rightarrow k_1 \leq k_3\)
  • Anti-symmetric: \(\forall (k_1,k_2) \in K^2, k_1 \leq k_2 \text{ and } k_1 \geq k_2 \Rightarrow k_1 = k_2\)
  • Total: \(\forall (k_1,k_2) \in K^2, k_1 \leq k_2 \text{ or } k_1 \geq k_2\)
template<
    class Key,
    class T,
    class Compare = std::less<Key>, // Comparator
    class Allocator = std::allocator<std::pair<const Key, T> >
> class map;

Maps

Unordered maps and sets

An unordered associative container is an associative container whose pairs std::pair<K,V> are stored based on hashing functions applied to keys.

Notes:

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

Principles

Hash tables

Maps

Redimensioning

Maps

Maps

Complexities summary


Complexity of operations for associative container models
Method Ordered map / set Unordered map /set
size() \(\Theta(1)\) \(\Theta(1)\)
at() / find() \(\Theta(\log(n))\) \(\Theta(1)\) (average case) / \(\Theta(n)\) (worst case)
operator[] / insert() \(\Theta(\log(n))\) \(\Theta(1)\) (amortized) / \(\Theta(n)\) (worst case)
erase() \(\Theta(\log(n))\) \(\Theta(1)\) (average case) / \(\Theta(n)\) (worst case)
lower_bound() / upper_bound() \(\Theta(\log(n))\)


Frédéric Pennerath,