C++ 学习笔记之(11) - 关联容器

C++ 学习笔记之(11) - 关联容器

关联容器和顺序容器有着根本的不同,关联容器中的元素是按关键字来保存和访问的,与之相对,顺序容器中的而元素是按照他们在容器中的位置来顺序保存和访问的。

关联容器支持高效的关键字查找和访问,关键字起到索引的作用。标准库提供了8个关联容器,它们的不同体现在三个维度上

  • 或者是set,或者是map
  • 或者要求不重复的关键字; 或者允许重复的关键字,容器名字包含multi
  • 按顺序保存元素; 或者无序保存,其容器名字包含unordered
    associative_container_types

关联容器概述

关联容器支持一些前面所讲的普通容器操作,但不支持顺序容器的位置相关的操作,比如push_back等,因为关联容器中元素是按关键字存储的。关联容器还支持一下特有的操作好类型别名

定义关联容器

类似顺序容器,关联容器也是模板,故比如定义map需要指定关键字和值的类型。set需要知道关键字类型。

map<string, size_t> word_count;  // 空容器
map<string, size_t> author_count = { {"Austen", 2}, {"Dickens", 5} };
set<string> exclude = {"the", "but"};  // 列表初始化

关键字类型的要求

对于有序容器(map, multimap, set, multiset) , 关键字必须定义元素比较的方法。默认标准库使用关键字类型的<运算符比较两个关键字。

  • 可以提供自定义操作来代替关键字上的<运算符, 所提供的操作必须在关键字类型上定义一个 严格弱序, 即小于等于

    • 两个关键字不能同时小于等于对方
    • k1小于等于k2k2小于等于k3, 则k1小于等于k3
    • 若两个关键字,任何一个都不小于等于对方,则等价
  • 若定义有序关联容器要对关键字类型使用自定义比较函数时,需要提供关键字类型以及比较操作类型

    // bookstore 中的元素以 ISBN 的顺序进行排列,允许重复. compareIsbn用来比较Sales_data对象ISBN
    multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);

pair 类型

头文件utility中, 保存两个成员。类似容器,pair是模板,定义时序提供两个类型名

pair_operations

关联容器操作

关联容器定义了额外的类型

associative_container_extra_type_alias

我们用作用域运算符提取类型的成员

map<string, int>::key_type kt;  // kt 是 string

关联容器迭代器

当解引用关联容器迭代器时,得到类型为容器的value_type的值的引用

  • set中的元素是constmap中的元素是pair,其第一个成员是const

  • set的迭代器是const的, 虽然同时定义了iteratorconst_iterator类型

  • 通常不对关联容器使用泛型算法,因为关键字是const这一特性意味着不能将关联容器传递给修改或重排元素的算法
  • 若真要对关联容器使用算法, 则可将它当做源序列或目的序列,使用copy拷贝或inserter绑定调用其他算法

添加元素

关联容器的insert成员可向容器中添加元素或元素范围
associative_container_insert_operation

// 向 map 中插入元素的 4 种方法, 注意元素类型为 pair
word_count.insert({word, 1});
word_count.insert(make_pair(word, 1));
word_count.insert(pair<string, size_t>(word, 1));
word_count.insert(map<string, size_t>::value_type(word, 1));
  • 对于不包含重复元素的关联容器来说,insertemplace返回一个pairfirst为指向给定关键字元素的迭代器,secondbool,表示是否成功插入(已在容器的不插入,返回false
  • 对包含重复元素的关联容器来说,比如multimapmultiset, 总能成功插入,故无需返回bool

删除元素

associative_container_erase_item

map 的下标操作

  • set不支持下标操作,也不能对multimapunordered_multimap进行下标操作,因为可能有多个值与之关联
  • vectorstring不同,map的下标运算符返回的类型与解引用map迭代器得到的类型不同。对map下标操作,获取mapped_type对象,而解引用map迭代器时,获取value_type对象

map_and_unordered_map_index_operation

访问元素

associative_container_find_item_operations

  • 下标运算符在关键字不在的情况下,会插入新元素,进行值初始化,若不想更改map,可使用find

  • multimapmultiset中有多个元素具有给定关键字,则会相邻存储

    // 三种方法获取相同元素
    multimap<string, int> mm = { {"a", 1}, {"b", 2}, {"a", 3}, {"a", 4} };
    string search_item("a");
    // 方法一:find 和 count
    for(auto iter = mm.find(search_item), cnt = mm.count(search_item); cnt; ++iter, --cnt)
        cout << iter->second << endl;
    auto cnt = mm.count(search_item);  // 关键字是 a 的元素个数
    auto iter = mm.find(search_item);  // 关键字是 a 的第一个元素
    for(; cnt; ++iter, --cnt)
    cout << iter->second << endl;  // 输出 1, 3, 4
    
    // 方法2:使用 lower_bound 和 upper_bound
    // lower_bound 返回指向第一个具有给定关键字的位置; upper_bound 返回最后一个匹配给定关键字的元素之后的位置。若没有元素与关键字匹配,这两个函数返回相同迭代器
    for(auto beg = authors.lower_bound(search_item), end = authors.upper_bound(search_item); beg != end; ++beg)
        cout << beg->second << endl;  // 输出 1, 3, 4
    
    // 方法3:使用 equal_range
    // equal_range 返回一个迭代器 pair, 若关键字存在,则第一个迭代器返回指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置
    for(auto pos = authors.equal_range(search_item); pos.first != pos.second; ++pos.first)
        cout << pos.first->second << endl;  // 输入 1, 3, 4,

无序容器

新标准定义了4个无序关联容器, 这些容器不使用比较运算符组织元素,而是使用哈希函数和关键字类型的==运算符

管理桶

  • 无序容器在存储上组织为一组桶,每个桶保存零个或多个元素,无序容器使用哈希函数将元素映射到桶。

  • 不能直接定义关键字类型为自定义类类型的无序容器,因为要使用哈希模板,所以必须提供自定义的hash模板版本(需要提供函数来替代==运算符和哈希值计算函数)

unordered_container_manage_operations

结语

  • 关联容器通过关键字高效查找和提取元素,而顺序容器通过位置访问元素

  • 标准库定义了8个关联容器,容器的类别与三个维度有关

  • 有序容器使用比较函数来比较关键字,从而将元素按顺序存储,默认比较操作采用关键字类型的<运算符。无序容器使用关键字类型的==运算符和一个hash<key_type>类型的对象来组织元素
  • 允许重复关键字的容器名字都包含multi, 使用哈希技术的容器名字都以unordered开头
  • 无论在有序容器还是无序容器中,具有相同关键字的元素都是相邻存储的

猜你喜欢

转载自blog.csdn.net/u011221820/article/details/80138077
今日推荐