C++中的unordered_map和map区别

简介: `unordered_map` 类模板和 `map` 类模板都是描述了这么一个对象:它是由 `std::pair<const Key, value>` 组成的可变长容器,这个容器中每个元素存储两个对象,也就是 `key` - `value` 对。

1. unordered_map

在头文件上,引入 <unordered_map> 来使用它。对于 unordered_map 而言,最大的特点在于内部实现上,使用到了哈希表(散列表、hash_table )来进行映射存储,它的模板类声明及其参数如下:

/**
 * 程序来自STL源码 bits/unordered_map.h
 */
template<typename _Key,  // key 类型 
        typename _Tp,    // value 类型
        typename _Hash = hash <_Key>,     // 哈希函数
        typename _Pred = equal_to <_Key>, // 用于比较两者是否相同的函数
        typename _Alloc = allocator <std::pair<const _Key, _Tp>>> // 分配器,描述了容器在内存管理上的细节,不应该自己来处理,除非写自己的容器
class unordered_map {
};

unordered_map 内部,使用的 Hash Table 对数据进行组织,通过把键值 key 映射到 hash 表中的一个位置进行访问,根据 hash 函数的特点, unordered_map 对于元素查找的时间复杂度可以达到 O(1) ,但是,它的元素排列是无序的。具体例子如下:

int main() {
    using namespace std;
    // 首先创建一个无序 map,它的 key 使用 int 类型,value 使用 string 类型
    unordered_map<int, string> unorderedMap;    
   
    // 三种插入新元素的方法,“茴”字有三种写法~
    unorderedMap.insert(make_pair(0, "Alice")); 
    unorderedMap[1] = "Bob";
    unorderedMap.insert(unordered_map<int, string>::value_type(2, "Candy"));
 
    // 对内部元素挨个输出
    for (auto iter = unorderedMap.begin(); iter != unorderedMap.end(); iter++) {
        cout << iter->first << " - " << iter->second << endl;
        /*
         * >: 输出如下,可以得知它们在 key 的排序上并没有顺序
         * 2 - Candy
         * 0 - Alice
         * 1 - Bob
         */
    }
}

unordered_map 由于建立了哈希表,所以它在最开始建立的时候比较耗时间,但是它查询速度快呀~,一般情况下用 unordered_map 是没有问题的。

2. map

对于 map 而言,首先在头文件上,引用 <map> 进来,然后使用。它的类模板声明以及部分函数声明如下:

/**
 * 程序来自C++源码 bits/stl_map.h
 */
template<typename _Key,  // key 类型
        typename _Tp,    // value 类型
        typename _Compare = std::less<_Key>, // 用于比较两个元素的比较函数
        typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > > // 分配器,同样的描述了容器在内存管理上的细节,不应该自己来处理,除非写自己的容器
class map {
private:
    /// 将一个红黑树转换成 [multi]map.
    typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
    rebind<value_type>::other _Pair_alloc_type;

    typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
    key_compare, _Pair_alloc_type> _Rep_type;
};

map 的内部,使用了红黑树red-black tree)来组织数据,因此默认的就已经实现了数据的排序。从下面例子中可以看出,它默认实现了在 key 上排序实现递增:

int main() {
    map<int, string> mapper;
    mapper.insert(make_pair(0, "Alice"));
    mapper[1] = "Bob";
    mapper.insert(map<int, string>::value_type(2, "Candy"));
    for (auto &iter : mapper) {
        cout << iter.first << " - " << iter.second << endl;
        /*
         * >: 输出如下,很明显的,它们在 key 的排序上是递增排列的
         * 0 - Alice
         * 1 - Bob
         * 2 - Candy
         */
    }
}

不过,在存储上 map 却比较占用空间,因为在红黑树中,每一个节点都要额外保存父节点和子节点的连接,因此使得每一个节点都占用较大空间来维护红黑树性质。

3. 总结

两种数据结构特点如下表格~

unordered_map map
查找 ,Average:O(1) ,Worst Case:O(n) 恒定的 log(n)
插入 和上面一样 log(n) + 平衡二叉树所用时间
删除 还和上面一样 log(n) + 平衡二叉树所用时间
是否排序 不排序 排序
实现方法 哈希表 红黑树
适用于 查找操作频率高 要求结果有序(按 key 排序)
目录
相关文章
|
4月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
404 1
|
8月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
247 2
|
8月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
471 73
|
5月前
|
算法 Java 数据库连接
Java 与 C++ 区别深入剖析及应用实例详解
本文深入剖析了Java和C++两种编程语言的区别,从编译与执行机制、面向对象特性、数据类型与变量、内存管理、异常处理等方面进行对比,并结合游戏开发、企业级应用开发、操作系统与嵌入式开发等实际场景分析其特点。Java以跨平台性强、自动内存管理著称,适合企业级应用;C++则因高性能和对硬件的直接访问能力,在游戏引擎和嵌入式系统中占据优势。开发者可根据项目需求选择合适语言,提升开发效率与软件质量。附面试资料链接:[点此获取](https://pan.quark.cn/s/4459235fee85)。
529 0
|
5月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
247 0
|
7月前
|
JavaScript 前端开发 开发者
flat、flatmap与map的用法区别
本文介绍了 JavaScript 数组方法 `flat()`、`flatMap()` 和 `map()` 的用法及区别。`flat()` 可按指定深度递归展平数组,参数为深度,默认一层;`flatMap()` 结合了 `map()` 和 `flat()` 功能,返回一维数组,长度可能不同于原数组;而 `map()` 返回与原数组长度一致的新数组。通过多个代码示例展示了三者的功能和差异,帮助开发者更好地理解和使用这些方法。
814 0
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
306 3
【C++】map、set基本用法
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
251 5
|
存储 C++ 容器
【C++】map的模拟实现
C++中的`map`是STL中的一种关联容器,存储键值对且键唯一。`map`基于红黑树实现,自动按键排序,支持动态调整、复杂数据类型、丰富的成员函数及双向迭代器。插入、查找等操作保证了对数时间复杂度,适用于需要快速查找和有序存储的场景。
171 3