C++容器:std::unordered_map, std::array, std::tuple
·
1. std::unordered_map - 哈希表
为什么需要unordered_map?
// std::map (红黑树,有序) vs std::unordered_map (哈希表,无序)
#include <map>
#include <unordered_map>
#include <iostream>
void compare_containers() {
std::map<int, std::string> ordered_map = {
{3, "three"}, {1, "one"}, {2, "two"}
};
std::unordered_map<int, std::string> unordered_map = {
{3, "three"}, {1, "one"}, {2, "two"}
};
std::cout << "std::map (ordered): ";
for (const auto& p : ordered_map) {
std::cout << p.first << " "; // 输出: 1 2 3
}
std::cout << "\nstd::unordered_map (unordered): ";
for (const auto& p : unordered_map) {
std::cout << p.first << " "; // 输出: 3 1 2 (顺序不确定)
}
}
基本用法
#include <unordered_map>
#include <string>
void unordered_map_basics() {
// 创建和初始化
std::unordered_map<std::string, int> age_map = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
// 插入元素
age_map["David"] = 28; // 使用下标操作符
age_map.insert({"Eve", 32}); // 使用insert
age_map.emplace("Frank", 40); // 使用emplace (C++11)
// 访问元素
std::cout << "Alice's age: " << age_map["Alice"] << std::endl;
std::cout << "Bob's age: " << age_map.at("Bob") << std::endl;
// 检查元素是否存在 (C++20)
if (age_map.contains("Charlie")) {
std::cout << "Charlie exists" << std::endl;
}
// 遍历
for (const auto& [name, age] : age_map) { // C++17结构化绑定
std::cout << name << ": " << age << std::endl;
}
// 删除元素
age_map.erase("David");
// 大小和容量
std::cout << "Size: " << age_map.size() << std::endl;
std::cout << "Bucket count: " << age_map.bucket_count() << std::endl;
}
哈希表性能特性
#include <unordered_map>
#include <iostream>
void hash_performance() {
std::unordered_map<int, std::string> map;
// 设置负载因子和重新哈希
map.max_load_factor(0.7f); // 设置最大负载因子
map.rehash(100); // 预分配桶空间
// 插入大量数据并观察性能
for (int i = 0; i < 1000; ++i) {
map[i] = "value_" + std::to_string(i);
}
std::cout << "Load factor: " << map.load_factor() << std::endl;
std::cout << "Bucket count: " << map.bucket_count() << std::endl;
// 查找性能测试
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000; ++i) {
auto it = map.find(i % 1000);
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Find time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< "μs" << std::endl;
}
自定义哈希函数
#include <unordered_map>
#include <string>
struct Person {
std::string name;
int age;
// 相等比较运算符
bool operator==(const Person& other) const {
return name == other.name && age == other.age;
}
};
// 自定义哈希函数
struct PersonHash {
std::size_t operator()(const Person& p) const {
return std::hash<std::string>()(p.name) ^ (std::hash<int>()(p.age) << 1);
}
};
void custom_hash_example() {
std::unordered_map<Person, std::string, PersonHash> person_map;
person_map[{"Alice", 25}] = "Engineer";
person_map[{"Bob", 30}] = "Designer";
for (const auto& [person, job] : person_map) {
std::cout << person.name << " (" << person.age << "): " << job << std::endl;
}
}
2. std::array - 固定大小数组
为什么需要std::array?
// 传统C数组 vs std::array
void compare_arrays() {
// 传统C数组
int c_array[5] = {1, 2, 3, 4, 5};
// 问题:不知道大小(需要额外变量记录)
// 问题:会退化为指针
// std::array
std::array<int, 5> std_array = {1, 2, 3, 4, 5};
// 优点:知道大小,支持STL算法,不会退化为指针
std::cout << "Size: " << std_array.size() << std::endl;
}
基本用法
#include <array>
#include <algorithm>
#include <iostream>
void array_basics() {
// 创建和初始化
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::array<std::string, 3> str_arr = {"hello", "world", "!"};
// 访问元素
std::cout << "First: " << arr.front() << std::endl; // 1
std::cout << "Last: " << arr.back() << std::endl; // 5
std::cout << "Index 2: " << arr[2] << std::endl; // 3
std::cout << "Index 3: " << arr.at(3) << std::endl; // 4 (带边界检查)
// 迭代器支持
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 范围for循环
for (int value : arr) {
std::cout << value << " ";
}
std::cout << std::endl;
// STL算法支持
std::sort(arr.begin(), arr.end());
auto count = std::count_if(arr.begin(), arr.end(),
[](int x) { return x % 2 == 0; });
// 多维数组
std::array<std::array<int, 3>, 2> matrix = {
{{1, 2, 3},
{4, 5, 6}}
};
}
编译期特性
#include <array>
#include <type_traits>
constexpr std::array<int, 5> create_array() {
return {1, 2, 3, 4, 5};
}
void compile_time_features() {
// 编译期大小信息
constexpr size_t size = std::tuple_size<std::array<int, 5>>::value;
static_assert(size == 5, "Size must be 5");
// 编译期创建和操作
constexpr auto arr = create_array();
static_assert(arr[0] == 1, "First element must be 1");
// 用于模板元编程
using MyArray = std::array<int, 10>;
using ValueType = typename MyArray::value_type;
static_assert(std::is_same_v<ValueType, int>, "Value type must be int");
}
3. std::tuple - 异构元组
为什么需要std::tuple?
// 传统方式:需要定义结构体
struct Person {
std::string name;
int age;
double salary;
};
// 使用tuple:不需要预先定义类型
void tuple_vs_struct() {
// 定义结构体
Person p{"Alice", 25, 50000.0};
// 使用tuple
std::tuple<std::string, int, double> person{"Alice", 25, 50000.0};
// 临时、一次性的数据结构用tuple更方便
}
基本用法
#include <tuple>
#include <string>
#include <iostream>
void tuple_basics() {
// 创建tuple
std::tuple<int, std::string, double> t1(1, "hello", 3.14);
auto t2 = std::make_tuple(2, "world", 2.71); // 自动推导类型
std::tuple t3(3, "!", 1.41); // C++17 CTAD
// 访问元素 (C++11方式)
std::cout << "get<0>: " << std::get<0>(t1) << std::endl;
std::cout << "get<1>: " << std::get<1>(t1) << std::endl;
std::cout << "get<2>: " << std::get<2>(t1) << std::endl;
// 修改元素
std::get<0>(t1) = 100;
// 获取tuple信息
std::cout << "Tuple size: " << std::tuple_size<decltype(t1)>::value << std::endl;
// 类型获取
using FirstType = std::tuple_element<0, decltype(t1)>::type;
static_assert(std::is_same_v<FirstType, int>, "First type must be int");
}
结构化绑定 (C++17)
#include <tuple>
#include <string>
void structured_binding() {
auto person = std::make_tuple("Alice", 25, 50000.0);
// 传统方式
std::string name = std::get<0>(person);
int age = std::get<1>(person);
double salary = std::get<2>(person);
// C++17结构化绑定
auto [name2, age2, salary2] = person;
// 引用绑定(可以修改原tuple)
auto& [name_ref, age_ref, salary_ref] = person;
name_ref = "Bob"; // 修改原tuple
// 用于函数返回多个值
auto get_person_info = []() -> std::tuple<std::string, int, double> {
return {"Charlie", 30, 60000.0};
};
auto [name3, age3, salary3] = get_person_info();
}
tuple的实用操作
#include <tuple>
#include <iostream>
void tuple_operations() {
auto t1 = std::make_tuple(1, "hello", 3.14);
auto t2 = std::make_tuple(2, "world", 2.71);
// 比较操作
if (t1 < t2) { // 按字典序比较
std::cout << "t1 < t2" << std::endl;
}
// 交换
std::swap(t1, t2);
// 连接tuple (C++11)
auto t3 = std::tuple_cat(t1, t2, std::make_tuple("extra"));
// 解包调用函数
void print_info(int id, const std::string& name, double value);
std::apply(print_info, t1);
// 遍历tuple (需要模板元编程)
}
实际应用场景
#include <tuple>
#include <vector>
#include <algorithm>
// 1. 函数返回多个值
std::tuple<bool, std::string, int> process_data() {
// 处理逻辑...
return {true, "success", 42};
}
// 2. 多值排序
void multi_criteria_sort() {
std::vector<std::tuple<std::string, int, double>> people = {
{"Alice", 25, 50000.0},
{"Bob", 30, 45000.0},
{"Charlie", 25, 55000.0}
};
// 按年龄升序,薪水降序
std::sort(people.begin(), people.end(),
[](const auto& a, const auto& b) {
if (std::get<1>(a) != std::get<1>(b)) {
return std::get<1>(a) < std::get<1>(b);
}
return std::get<2>(a) > std::get<2>(b);
});
}
// 3. 变参模板的存储
template<typename... Args>
class Event {
private:
std::tuple<Args...> data;
public:
Event(Args... args) : data(std::make_tuple(args...)) {}
template<size_t Index>
auto get() const -> const std::tuple_element_t<Index, std::tuple<Args...>>& {
return std::get<Index>(data);
}
};
4. 常见问题
Q1: unordered_map 和 map 的区别?
答:
-
std::map:红黑树实现,元素有序,查找时间O(log n) -
std::unordered_map:哈希表实现,元素无序,平均查找时间O(1),最坏O(n) -
选择:需要有序用map,需要快速查找用unordered_map
Q2: std::array 相比C数组有什么优点?
答:
-
知道自己的大小(
size()方法) -
支持STL算法和迭代器
-
不会退化为指针
-
更好的类型安全
-
支持复制和赋值
Q3: 什么时候使用std::tuple?
答:
-
函数需要返回多个值时
-
临时性的异构数据集合
-
模板元编程中
-
避免定义只使用一次的结构体
Q4: 如何选择不同的容器?
答:
-
需要键值对:
map(有序)或unordered_map(快速) -
固定大小数组:
std::array -
动态数组:
std::vector -
异构数据:
std::tuple或std::variant
Q5: C++17对这些容器有什么增强?
答:
-
unordered_map:contains()方法 -
std::array:to_array()函数(C++20) -
std::tuple:结构化绑定,CTAD(类模板参数推导)
5. 最佳实践
使用auto简化代码
void modern_usage() {
// 使用auto避免冗长的类型声明
auto scores = std::unordered_map<std::string, int>{
{"Alice", 95}, {"Bob", 88}, {"Charlie", 92}
};
auto points = std::array{1, 2, 3, 4, 5}; // C++17 CTAD
auto person = std::make_tuple("David", 28, 75000.0);
// 结构化绑定
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << std::endl;
}
}
性能考虑
void performance_considerations() {
// unordered_map: 预分配空间
std::unordered_map<int, std::string> large_map;
large_map.reserve(10000); // 预分配,避免重新哈希
// array: 栈上分配,速度快但大小固定
std::array<int, 1000> fast_array; // 栈上分配
// tuple: 编译期确定,无运行时开销
constexpr auto config = std::make_tuple(1024, 768, "Window Title");
}
总结
C++11引入的这些容器大大增强了标准库的能力:
-
✅ std::unordered_map:高性能哈希表,快速查找
-
✅ std::array:类型安全的固定大小数组
-
✅ std::tuple:灵活的异构数据容器
-
✅ 现代C++特性:支持auto、结构化绑定、CTAD等
-
✅ 性能优势:编译期优化,运行时高效
更多推荐



所有评论(0)