Boost.Bimap 是 C++ Boost 库中的一个组件,它提供了一种双向映射的容器,即键和值之间的双向映射。这意味着可以通过键查找值,也可以通过值查找键。Boost.Bimap 提供了一种方便的方式来管理这种键-值对之间的关系,尤其适用于需要频繁进行双向查找的情况。
1. 操作视图
Boost Bimap 的三种视图分别是左集合视图 (left set view)、右集合视图 (right set view) 和关系视图 (relation view)。
- 左集合视图:左集合中的元素为 key,右集合中的元素为 value
- 右集合视图:右集合中的元素为 key,左集合中的元素为 value
- 关系视图:key 和 value 作为一个整体元素存在
#if 1 #include <boost/bimap/bimap.hpp> #include <iostream> void test() { // 创建双向映射容器 using container_type = boost::bimaps::bimap<int, std::string>; container_type map; // bimap 容器添加、删除、查找等操作都分为三个视图 // 左视图 map.left.insert(container_type::left_value_type(1006, "张三")); map.left.insert(std::make_pair(1005, "李四")); for (container_type::left_const_iterator iter = map.left.begin(), end = map.left.end(); iter != end; ++iter) { std::cout << "key:" << iter->first << " value:" << iter->second << std::endl; } std::cout << "-------------------" << std::endl; // 右视图 map.right.insert(container_type::right_value_type("王五", 1004)); map.right.insert(std::make_pair("赵六", 1003)); for (auto iter = map.right.begin(), end = map.right.end(); iter != end; ++iter) { std::cout << "key:" << iter->first << " value:" << iter->second << std::endl; } std::cout << "-------------------" << std::endl; // 关系视图 map.insert(container_type::value_type(1002, "李七")); map.insert({ 1001, "赵八" }); for (container_type::const_iterator iter = map.begin(), end = map.end(); iter != end; ++iter) { std::cout << "key:" << iter->left << " value:" << iter->right << std::endl; } std::cout << "-------------------" << std::endl; } int main() { test(); return 0; } #endif
程序输出结果:
key:1005 value:李四 key:1006 value:张三 ------------------- key:李四 value:1005 key:王五 value:1004 key:张三 value:1006 key:赵六 value:1003 ------------------- key:1001 value:赵八 key:1002 value:李七 key:1003 value:赵六 key:1004 value:王五 key:1005 value:李四 key:1006 value:张三 -------------------
注意:插入数据时,使用视图的 value_type 来构造插入对象,比 std::make_pair 效率高。这是因为 std::pair 需要转换为 bimap pair,而 value_type 则不需要。
2. 集合类型
Boost Bimap 提供了多种集合类型 (collection types),这些集合类型规定了不同的数据存储和访问方式,每种集合类型都有其特定的用途和优势,使开发者能够根据实际场景灵活的选择和组合。
2.1 使用方法
Boost Bimap 支持的集合类型如下表所示,我们可以根据需求来选择不同的左右集合类型组合,默认左右集合都是使用 set_of 进行存储和访问。
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/set_of.hpp> #include <boost/bimap/multiset_of.hpp> #include <boost/bimap/unordered_set_of.hpp> #include <boost/bimap/unordered_multiset_of.hpp> #include <boost/bimap/list_of.hpp> #include <boost/bimap/vector_of.hpp> #include <boost/bimap/support/lambda.hpp> #include <iostream> #include <algorithm> using namespace boost::bimaps; using namespace std; // 1. set_of multiset_of void test01() { // 左视图有序,唯一、右视图有序,不唯一 using container_type = bimap<set_of<int>, multiset_of<string>>; container_type map; // 注意: 添加的元素必须满足所有视图的约束(左视图、右视图、关系视图) map.left.insert(container_type::left_value_type(1001, "张三")); map.left.insert(container_type::left_value_type(1001, "李四")); map.right.insert(container_type::right_value_type("王五", 1003)); map.right.insert(container_type::right_value_type("王五", 1004)); map.right.insert(container_type::right_value_type("赵六", 1005)); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 查找指定 key 元素 // 查找失败返回 end 迭代器 auto position = map.left.find(1001); if (position != map.left.end()) { cout << position->first << " " << position->second << endl; cout << "------------" << endl; } // 删除元素 map.left.erase(position); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 查找全部相同元素 auto pair = map.right.equal_range("王五"); for_each(pair.first, pair.second, [](auto& item) {cout << item.first << " " << item.second << endl; }); cout << "------------" << endl; // 查找区间元素 [ // 1003, 1004] auto iter_beg = map.left.lower_bound(1003); // 大于等于 auto iter_end = map.left.upper_bound(1004); // 大于 for_each(iter_beg, iter_end, [](auto& item) {cout << item.first << " " << item.second << endl; }); cout << "------------" << endl; // auto pair = map.left.range(1001 <= _key, _key <= 1003); // [1001, 1003] auto range_pair = map.left.range(unbounded, _key < 1005); // (-inf, 1005) cout << range_pair.first->first << " " << range_pair.second->first << endl; cout << "------------" << endl; } void test02() { // 左视图无序,唯一、右视图无序,不唯一 using container_type = bimap<unordered_set_of<int>, unordered_multiset_of<string>>; container_type map; map.left.insert(container_type::left_value_type(1001, "张三")); map.left.insert(container_type::left_value_type(1001, "李四")); map.right.insert(container_type::right_value_type("王五", 1003)); map.right.insert(container_type::right_value_type("王五", 1004)); map.right.insert(container_type::right_value_type("赵六", 1005)); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 查找指定 key 元素 // 查找失败返回 end 迭代器 auto position = map.left.find(1001); if (position != map.left.end()) { cout << position->first << " " << position->second << endl; cout << "------------" << endl; } // unordered_set 不存在 lower_bound 、upper_bound、range 函数 auto pair = map.right.equal_range("王五"); for_each(pair.first, pair.second, [](auto& item) {cout << item.first << " " << item.second << endl; }); cout << "------------" << endl; } void test03() { // 左视图顺序、右视图随机访问 using container_type = bimap<list_of<int>, vector_of<string>>; container_type map; map.left.push_back(container_type::left_value_type(1001, "张三")); map.left.push_back(container_type::left_value_type(1001, "李四")); map.right.push_front(container_type::right_value_type("王五", 1003)); map.right.push_front(container_type::right_value_type("王五", 1004)); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 修改元素 map.left.begin()->second = "GGG"; // 排序 map.left.sort(std::greater<int>()); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 删除元素 map.left.erase(map.left.begin()); for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; container_type temp; temp.push_back(container_type::value_type(2001, "aaa")); temp.push_back(container_type::value_type(2002, "bbb")); temp.push_back(container_type::value_type(2003, "ccc")); temp.push_back(container_type::value_type(2004, "ddd")); temp.push_back(container_type::value_type(2005, "ddd")); // 合并容器 map.left.merge(temp.left); cout << "------------" << endl; for_each(map.begin(), map.end(), [](auto& item) { cout << item.left << " " << item.right << endl; }); cout << "------------" << endl; // 右视图访问 cout << map.right[0].first << " " << map.right[0].second << endl; } int main() { test03(); return 0; } #endif
2.2 集合参数
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/set_of.hpp> #include <boost/bimap/unordered_set_of.hpp> #include <boost/functional/hash.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; class Person { public: Person(string name, int age) { m_name = name; m_age = age; } public: std::string m_name; int m_age; }; struct LessPerson { bool operator()(const Person& p1, const Person& p2) const { return p1.m_age < p2.m_age; } }; void test01() { using container_type = bimap<int, set_of<Person, LessPerson>>; container_type map; // 由于右集合使用 set 容器,该容器会自动排序,对自定义类型要重载 < 运算符或者一个谓词作为模板参数 map.insert(container_type::value_type(1001, { "Smith", 100 })); map.insert(container_type::value_type(1002, { "Obama", 200 })); map.insert(container_type::value_type(1003, { "Polly", 150 })); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right.m_name << " " << item.right.m_age << endl; }); } struct EqualPerson { bool operator()(const Person& p1, const Person& p2) const { return p1.m_name == p2.m_name && p1.m_age == p2.m_age; } }; struct HashPerson { size_t operator()(const Person& person) const { return boost::hash<string>()(person.m_name); } }; void test02() { using container_type = bimap<int, unordered_set_of<Person, HashPerson, EqualPerson>>; container_type map; // 由于右集合使用哈希表,该容器要求元素能够被哈希,能够使用 < 比较,所以需要提供一个针对该类型的哈希函数,以及==比较规则 map.insert(container_type::value_type(1001, { "Smith", 100 })); map.insert(container_type::value_type(1002, { "Obama", 200 })); map.insert(container_type::value_type(1003, { "Polly", 150 })); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right.m_name << " " << item.right.m_age << endl; }); } int main() { test01(); cout << "------------" << endl; test02(); return 0; } #endif
程序执行结果:
1001 Smith 100 1002 Obama 200 1003 Polly 150 ------------ 1001 Smith 100 1002 Obama 200 1003 Polly 150
2.3 无约束类型
unconstrained_set_of 称作无约束集合类型,该集合类型并不提供任何对数据的操作。所以,将左视图或者右视图设置为无约束集合类型,可以实现:
- 禁用某个视图:禁用 bimap 中的一个视图可以提高操作的执行速度并减少内存消耗
- 实现单向映射:通过禁用一个视图,bimap 变成单向映射,而不是双向映射
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/unconstrained_set_of.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; void test() { // 相当于单向容器 using container_type = bimap<int, unconstrained_set_of<std::string>>; container_type map; map.insert(container_type::value_type(1001, "张三")); map.left.insert(container_type::left_value_type(1002, "李四")); // 无法使用右视图执行插入操作 for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); } int main() { test(); return 0; } #endif
程序执行结果:
1001 张三 1002 李四
3. 关系类型
Boost Bimap 容器的关系视图就是将键值对作为一个整体,一个元素对待,关系类型则是考虑如何存储这些关系(键值对)。
3.1 基本使用
默认情况下,Boost.Bimap 会基于其中一侧(左侧或右侧)的集合类型来确定关系集合的类型。例如:左集合类型是 set
,那么关系集合的类型也会是具有相同顺序的 set
。
然而,在某些情况下,用户可能希望对关系集合施加不同的约束或使用不同的排序方式。
- left_based:基于左侧集合类型。
- right_based:基于右侧集合类型。
- set_of_relation<>:有序集合。
- multiset_of_relation<>:有序多重集合,允许重复元素。
- unordered_set_of_relation<>:无序集合。
- unordered_multiset_of_relation<>:无序多重集合,允许重复元素。
- list_of_relation:列表形式的集合,保持插入顺序。
- vector_of_relation:向量形式的集合,允许快速随机访问。
- unconstrained_set_of_relation:不受约束的集合,可以在性能和内存使用方面进行优化。
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/set_of.hpp> #include <boost/bimap/multiset_of.hpp> #include <boost/bimap/unordered_set_of.hpp> #include <boost/bimap/unordered_multiset_of.hpp> #include <boost/bimap/vector_of.hpp> #include <boost/bimap/list_of.hpp> #include <boost/bimap/unconstrained_set_of.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; void test01() { // 使用 set 存储键值对 using container_type = bimap<unconstrained_set_of<int>, unconstrained_set_of<string>, set_of_relation<>>; container_type map; // 插入元素必须满足三个视图的约束 map.insert(container_type::value_type(1001, "张三")); map.insert(container_type::value_type(1002, "李四")); map.insert(container_type::value_type(1001, "李四")); map.insert(container_type::value_type(1002, "李四")); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "-------------" << endl; } void test02() { // 使用哈希表存储键值对 using container_type = bimap<unconstrained_set_of<int>, unconstrained_set_of<string>, unordered_set_of_relation<>>; container_type map; map.insert(container_type::value_type(1001, "张三")); map.insert(container_type::value_type(1002, "李四")); map.insert(container_type::value_type(1001, "李四")); map.insert(container_type::value_type(1002, "李四")); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "-------------" << endl; } void test03() { // 使用 vector 存储键值对 using container_type = bimap<unconstrained_set_of<int>, unconstrained_set_of<string>, vector_of_relation>; container_type map; map.push_back(container_type::value_type(1001, "张三")); map.push_back(container_type::value_type(1002, "李四")); map.push_back(container_type::value_type(1001, "李四")); map.push_back(container_type::value_type(1002, "李四")); for (int i = 0; i < map.size(); ++i) { cout << map[i].left << " " << map[i].right << endl; } cout << "-------------" << endl; } void test04() { // 移除关系视图,则无法使用关系视图进行操作 using container_type = bimap<int, string, unconstrained_set_of_relation>; container_type map; map.left.insert(container_type::left_value_type(1001, "张三")); map.left.insert(container_type::left_value_type(1002, "李四")); map.left.insert(container_type::left_value_type(1001, "李四")); map.left.insert(container_type::left_value_type(1002, "李四")); for_each(map.left.begin(), map.left.end(), [](const auto& item) { cout << item.first << " " << item.second << endl; }); cout << "-------------" << endl; } int main() { test01(); test02(); test03(); test04(); return 0; } #endif
3.2 关系参数
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/set_of.hpp> #include <boost/bimap/unordered_set_of.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; class Person { public: Person(string name, int age) { m_name = name; m_age = age; } public: string m_name; int m_age; }; template<class Rel> struct CompareRelation { bool operator()(Rel r1, Rel r2) const { cout << r1.left << " " << r1.right.m_name << " " << r1.right.m_age << endl; cout << r2.left << " " << r2.right.m_name << " " << r2.right.m_age << endl; cout << "------------" << endl; return r1.left < r2.left; } }; struct ComparePerson { bool operator()(const Person& p1, const Person& p2) const { return p1.m_age < p2.m_age; } }; void test01() { // 对于右视图:由于使用 set,所以需要提供 Person 的 < 比较规则 // 对于关系视图:由于使用 set,所以需要提供 relation 的 < 比较规则 // _relation 是一个占位符,当模板实例化时,占位符会被实际的 relation 类型替换 using container_type = bimap<int, set_of<Person, ComparePerson>, set_of_relation< CompareRelation<_relation> >>; container_type map; map.insert(container_type::value_type(1001, { "张三", 18 })); map.insert(container_type::value_type(1002, { "李四", 20 })); // 查找 map.find(container_type::value_type(1001, { "张三", 18 })); } template<class Rel> struct EqualRelation { bool operator()(Rel r1, Rel r2) const { return r1.left == r2.left && r1.right.m_name == r2.right.m_name && r1.right.m_age == r2.right.m_age; } }; template <class Rel> struct HashRelation { size_t operator()(Rel rel) const { // 将 {1001, {"张三", 20}} 封装成 tuple 对象 // 使用内 hash 函数对 tuple 对象进行 hash return boost::hash<std::tuple<int, int, string>>()(std::tuple<int, int, string>(rel.left, rel.right.m_age, rel.right.m_name)); } }; void test02() { using container_type = bimap<int, unconstrained_set_of<Person>, unordered_set_of_relation< HashRelation<_relation>, EqualRelation<_relation> >>; container_type map; map.insert(container_type::value_type(1001, { "张三", 18 })); map.insert(container_type::value_type(1002, { "李四", 20 })); // 查找 auto it = map.find(container_type::value_type(1002, { "李四", 20 })); if (it != map.end()) { cout << "find:" << it->left << " " << it->right.m_name << " " << it->right.m_age << endl; } } int main() { test01(); test02(); return 0; } #endif
程序执行结果:
1002 李四 20 1001 张三 18 ------------ 1001 张三 18 1002 李四 20 ------------ 1001 张三 18 1001 张三 18 ------------ 1001 张三 18 1001 张三 18 ------------ find:1002 李四 20
4. 视图标签
默认情况下,我们使用
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/set_of.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; void test() { // 给左右集合设置标签 using container_type = bimap<tagged<int, struct id>, set_of<tagged<string, struct name>>>; container_type map; // map.left // map.right map.by<id>(); map.by<name>(); // container_type::left_value_type // container_type::right_value_type map.by<id>().insert(container_type::map_by<id>::value_type(1001, "张三")); map.by<name>().insert(container_type::map_by<name>::value_type("李四", 1002)); // container_type::left_const_iterator // container_type::right_const_iterator for (container_type::map_by<id>::const_iterator it = map.by<id>().begin(); it != map.by<id>().end(); ++it) { // it->first // it->second cout << it->get<id>() << " " << it->get<name>() << endl; } cout << "---------" << endl; for (container_type::const_iterator it = map.begin(); it != map.end(); ++it) { // it->left // it->right cout << it->get<id>() << " " << it->get<name>() << endl; } } int main() { test(); return 0; } #endif
程序执行结果:
1001 张三 1002 李四 --------- 1001 张三 1002 李四
5. 附加信息
Boost Bimap 支持为每一个 relation 增加一个附加消息。
#if 1 #include <boost/bimap/bimap.hpp> #include <iostream> using namespace std; using namespace boost::bimaps; void test() { // using container_type = bimap<int, std::string, with_info<string>>; // using container_type = bimap<int, std::string, set_of_relation<>, with_info<string>>; using container_type = bimap<tagged<int, struct id>, tagged<string, struct name>, with_info<tagged<string, struct extra>>>; container_type map; map.insert(container_type::value_type(1001, "张三", "学生1信息")); map.insert(container_type::value_type(1002, "李四", "学生2信息")); map.insert(container_type::value_type(1003, "王五", "学生3信息")); // 修改附加信息 map.begin()->info = "修改信息"; for (auto& student : map) { cout << student.left << " " << student.right << " " << student.info << endl; cout << student.get<id>() << " " << student.get<name>() << " " << student.get<extra>() << endl; } } int main() { test(); return 0; } #endif
程序执行结果:
1001 张三 修改信息 1001 张三 修改信息 1002 李四 学生2信息 1002 李四 学生2信息 1003 王五 学生3信息 1003 王五 学生3信息
6. 实用函数
#if 1 #include <boost/bimap/bimap.hpp> #include <boost/bimap/support/lambda.hpp> #include <iostream> #include <type_traits> using namespace std; using namespace boost::bimaps; // 1. 修改 key 或者 data void test01() { using container_type = boost::bimaps::bimap<int, string>; container_type map; map.insert(container_type::value_type(1001, "张三")); map.insert(container_type::value_type(1002, "李四")); map.insert(container_type::value_type(1003, "王五")); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "---------" << endl; // 1. replace 修改 key 和 data map.left.replace_key(map.left.begin(), 2002); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "---------" << endl; map.left.replace_data(map.left.begin(), "王五-修改"); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "---------" << endl; // 2. modify 修改 key 和 data map.left.modify_key(map.left.begin(), _key = 1111); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "---------" << endl; map.left.modify_data(map.left.begin(), _data = "王五-修改"); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); cout << "---------" << endl; // 注意:迭代器类型要选择对应视图类型的迭代器 } // 2. 迭代器视图类型转换 void test02() { using container_type = boost::bimaps::bimap<int, string>; container_type map; map.insert(container_type::value_type(1001, "张三")); map.insert(container_type::value_type(1002, "李四")); map.insert(container_type::value_type(1003, "王五")); auto iter = map.begin(); auto left_iter = map.left.begin(); auto right_iter = map.right.begin(); // 转换到视图迭代器 auto up1 = map.project_up(left_iter); auto up2 = map.project_up(right_iter); cout << is_same<decltype(up1), container_type::iterator>::value << endl; cout << is_same<decltype(up2), container_type::iterator>::value << endl; // 转换到左视图迭代器 auto left1 = map.project_left(iter); auto left2 = map.project_left(right_iter); cout << is_same<decltype(left1), container_type::left_iterator>::value << endl; cout << is_same<decltype(left2), container_type::left_iterator>::value << endl; // 转到右视图迭代器 auto right1 = map.project_right(iter); auto right2 = map.project_right(left_iter); cout << is_same<decltype(right1), container_type::right_iterator>::value << endl; cout << is_same<decltype(right2), container_type::right_iterator>::value << endl; // 使用场景 map.replace_left(map.project_up(map.right.find("李四")), 2001); for_each(map.begin(), map.end(), [](const auto& item) { cout << item.left << " " << item.right << endl; }); } int main() { test01(); test02(); return 0; } #endif
程序执行结果:
1001 张三 1002 李四 1003 王五 --------- 1002 李四 1003 王五 2002 张三 --------- 1002 王五-修改 1003 王五 2002 张三 --------- 1003 王五 1111 王五-修改 2002 张三 --------- 1111 王五-修改 2002 张三 --------- 1 1 1 1 1 1 1001 张三 1003 王五 2001 李四