90 likes | 186 Views
Associative Containers. unordered_multimap. Associative containers support efficient key lookup vs. sequence containers, which lookup by position Associative containers differ in 3 design dimensions Ordered vs. unordered (tree vs. hash structured)
E N D
Associative Containers unordered_multimap • Associative containers support efficient key lookup • vs. sequence containers, which lookup by position • Associative containers differ in 3 design dimensions • Ordered vs. unordered (tree vs. hash structured) • We’ll look at ordered containers today, unordered next time • Set vs. map (just the key or the key and a mapped type) • Unique vs. multiple instances of a key map C 2 set C C 3 B 2 A 0 B 7 C 2 D 7 B D A 3 multimap A C 2 multiset C unordered_map B 2 D 7 B D A 0 B 7 C 2 A 3 C 5 A C unordered_multiset C A B C unordered_set A B C
Ordered Associative Containers • Ordered associative containers are tree structured • Insert/delete maintain sorted order, e.g. operator< • Don’t use sequence algorithms like sort or find with them • Already sorted, so sorting unnecessary (or harmful) • Find is more efficient (logarithmic time) as a container method • Ordered associative containers are bidirectional • Can iterate through them in either direction, find sub-ranges • Can use as source or destination for algorithms like copy set map C C 2 B B 2 D D 7 A A 3 multiset multimap C C 2 B B 2 D D 7 A A 3 C C 5
Set vs. Map • A set/multiset stores keys (the key is the entire value) • Used to collect single-level information (e.g., a set of words to ignore) • Avoid in-place modification of keys (especially in a set or multiset) • A map/multimap associates keys with mapped types • That style of data structure is sometimes called an associative array • Map subscripting operator takes key, returns reference to mapped type • E.g., string s = employees[id]; // returns employee name • If key does not exist, [] creates new entry with the key, value-initialized (0 if numeric, default initialized if class) instance of the mapped type set map C C 2 B B 2 D D 7 C C 2 A A 3 multiset multimap B B 2 D D 7 A A 3 C C 5
Unique vs. Multiple Keys • In set and map containers, keys are unique • In set, keys are the entire value, so every element is unique • In map, multiple keys may map to same value, but can’t duplicate keys • Attempt to insert a duplicate key is ignored by the container • In multiset and multimap containers, duplicate keys ok • Since containers are ordered, duplicates are kept next to each other • Insertion will always succeed, at appropriate place in the order set map C C 2 B B 2 D D 7 A A 3 multiset multimap C C 2 B B 2 D D 7 A A 3 C C 5
Key Types, Comparators, Strict Weak Ordering • Like sort algorithm, can modify container’s order … • … with any callable object that can be used correctly for sort • Must establish a strict weak ordering over elements • Two keys cannot both be less than each other (inequality), so comparison operator must return false if they are equal • If a < b and b < c then a < c (transitivity of inequality) • If !(a < b) and ! (b < a) then a == b (equivalence) • If a == b and b == c then a == c (transitivity of eqivalence) • Type of the callable object is used in container type • Cool example in LLM pp. 426 using decltype for a function • Could do this by declaring your own pointer to function type • But much easier to let compiler’s type inference figure it out for you
Callable Object Ordered Associative Containers #include <set> #include <string> #include <iterator> #include <algorithm> using namespace std; struct Employee { Employee (const char * n, int i) : name_(n), id_(i) {} string name_; int id_; }; ostream& operator<< (ostream & os, const Employee & e) { os << e.name_ << " " << e.id_ << “ "; return os; } // set needs this (orders by name then id) bool operator< (const Employee & e, const Employee & f) { return e.name_ < f.name_ || (e.name_ == f.name_ && e.id_ < f.id_); } // orders by id then name) struct EmployeeIdComp { bool operator() (const Employee & e, const Employee & f) { return e.id_ < f.id_ || (e.id_ == f.id_ && e.name_ < f.name_); } }; int main (int, char *[]) { vector<Employee> v; v.push_back(Employee("Claire", 23451)); v.push_back(Employee("Bob", 12345)); v.push_back(Employee("Alice", 54321)); cout << "v: " ; copy (v.begin(), v.end(), ostream_iterator<Employee>(cout)); // "v: Claire 23451 Bob 12345 Alice 54321 " set<Employee> s; s.insert(v.begin(), v.end()); cout << "s: " ; copy (s.begin(), s.end(), ostream_iterator<Employee>(cout)); // "s: Alice 54321 Bob 12345 Claire 23451" set<Employee, EmployeeIdComp> t; t.insert(v.begin(), v.end()); cout << "t: " ; copy (t.begin(), t.end(), ostream_iterator<Employee>(cout)); // "t: Bob 12345 Claire 23451 Alice 54321 “ return 0; } callable object type
Pair Type • Maps use pair template to hold key, mapped type • A pair can be used hold any two types • Maps use the key type as the 1st element of the pair • Maps use the mapped type as the 2nd element of the pair • Can compare pair variables using operators • Equivalence, less than, other relational operators • Can declare pair variables several different ways • Easiest uses initialization list (curly braces around values) • Can also default construct (value initialization) • Can also construct with two values • Can also use special make_pair function
Concluding Remarks • Use associative containers for key based lookup • Ordering of elements is maintained over the keys • Think ranges and ordering rather than position indexes • A sorted vector may be a better alternative (depends on which operations you will use most often, and their costs) • Ordered associative containers use strict weak order • Any callable object that acts like < over int can be used • Maps allow two-level (dictionary-like) lookup • Vs. sets which are used for “there or not there” lookup • Map uses a pair to associate key with mapped type • Can enforce uniqueness or allow duplicates • Duplicates are still stored in order, creating “equal ranges”