- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
c++ unordered_map collision handling , resize and rehash
这是我之前提出的一个问题,我发现我对 unordered_map 的实现方式有很多困惑。我相信很多其他人也和我一样困惑。根据我没有阅读标准就知道的信息:
Every unordered_map implementation stores a linked list to external nodes in the array of buckets... No, that is not at all the most efficient way to implement a hash map for most common uses. Unfortunately, a small "oversight" in the specification of unordered_map all but requires this behavior. The required behavior is that iterators to elements must stay valid when inserting or deleting other elements
我希望有人可以解释实现以及它如何符合 C++ 标准定义(在性能要求方面),如果它真的不是实现 HashMap 数据结构的最有效方法,如何改进它?
最佳答案
该标准有效地要求 std::unordered_set
和 std::unordered_map
的实现——以及它们的“多”兄弟——使用 open hashing aka separate chaining ,这意味着一个桶数组,每个桶都持有链表的头部†。这个要求很微妙:它是以下原因的结果:
max_load_factor()
为 1.0(这意味着只要 size()
超过 bucket_count()
的 1.0 倍,表格就会调整大小,并且如果没有链接,那将是不切实际的,因为随着 load_factor()
]( closed hashing aka open addressing ) 接近 1,与哈希表实现的另一个主要类别 - https://en.cppreference.com/w/cpp/container/unordered_map/load_factor - 的冲突变得势不可挡。
引用资料:
23.2.5/15: The
insert
andemplace
members shall not affect the validity of iterators if(N+n) < z * B
, whereN
is the number of elements in the container prior to the insert operation,n
is the number of elements inserted,B
is the container’s bucket count, andz
is the container’s maximum load factor.amongst the Effects of the constructor at 23.5.4.2/1:
max_load_factor()
returns1.0
.
† 为了在不越过任何空桶的情况下实现最优迭代,GCC 的实现用迭代器将桶填充到一个包含所有值的单链表中:迭代器立即指向之前的元素桶的元素,因此如果删除桶的最后一个值,可以重新连接那里的 next
指针。
关于你引用的文字:
No, that is not at all the most efficient way to implement a hash map for most common uses. Unfortunately, a small "oversight" in the specification of unordered_map all but requires this behavior. The required behavior is that iterators to elements must stay valid when inserting or deleting other elements
没有“疏忽”……所做的事情是经过深思熟虑的,并且是在充分意识到的情况下完成的。确实可以达成其他妥协,但是开放散列/链接方法对于一般用途来说是一个合理的妥协,它可以相当优雅地处理来自平庸散列函数的冲突,对于小或大的键/值类型不会太浪费,并处理任意多的 insert
/erase
对,而不会像许多封闭哈希实现那样逐渐降低性能。
作为意识的证据,来自 Matthew Austern's proposal here :
I'm not aware of any satisfactory implementation of open addressing in a generic framework. Open addressing presents a number of problems:
• It's necessary to distinguish between a vacant position and an occupied one.
• It's necessary either to restrict the hash table to types with a default constructor, and to construct every array element ahead of time, or else to maintain an array some of whose elements are objects and others of which are raw memory.
• Open addressing makes collision management difficult: if you're inserting an element whose hash code maps to an already-occupied location, you need a policy that tells you where to try next. This is a solved problem, but the best known solutions are complicated.
• Collision management is especially complicated when erasing elements is allowed. (See Knuth for a discussion.) A container class for the standard library ought to allow erasure.
• Collision management schemes for open addressing tend to assume a fixed size array that can hold up to N elements. A container class for the standard library ought to be able to grow as necessary when new elements are inserted, up to the limit of available memory.
Solving these problems could be an interesting research project, but, in the absence of implementation experience in the context of C++, it would be inappropriate to standardize an open-addressing container class.
特别是对于数据小到可以直接存储在存储桶中的仅插入表、未使用的存储桶的方便标记值以及良好的散列函数,封闭散列方法可能会快大约一个数量级,并且使用量大大减少内存,但这不是通用的。
哈希表设计选项及其含义的完整比较和详细说明不在 S.O. 的主题范围内。因为它太宽泛了,无法在这里正确解决。
关于c++ - std::unordered_map 是如何实现的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31112852/
我目前正在寻找 std::map 的更好替代方案,并且遇到了帖子标题中提到的类。有人可以阐明它们之间的区别,不是在性能/API 方面,而是在它们与当前和 future 的通信标准相关的地方。 最佳答案
我正在尝试使用一个 unordered_map 和另一个 unordered_map 作为键(自定义哈希函数)。我还添加了一个自定义的 equal 函数,尽管它可能并不需要。 代码没有达到我的预期,但
关闭。这个问题是not reproducible or was caused by typos .它目前不接受答案。 这个问题是由于错别字或无法再重现的问题引起的。虽然类似的问题可能是on-topi
我正在尝试从 unordered_map 中返回 unordered_map 的拷贝。 下面的代码更清楚地说明了我的问题: typedef std::unordered_map Foo; typede
我有一个类型为unordered_map的容器,我想确认要向 map 添加元素时应使用哪个版本。我希望它使用新呈现的旧值覆盖旧的值(如果存在),如果不存在则仅添加它。 我看到insert会在元素退出时
所以我试图将 unordered_map 设置为另一个 unordered_map 的值。 现在我遇到了无法将值放入第二个 unordered_map 的问题。 我的代码看起来像这样。 std::
我有一个数据结构,它是 unordered_map 的 unordered_map: typedef std::unordered_map map1; typedef std::unordered_m
我们正在用 C++ 为学校开发一个游戏项目。我负责 map 对象,它将包含炸弹、玩家、墙壁和盒子等实体。我的 map 中有 3 个容器: 玩家的 std::list(多个玩家可以站在同一个盒子上)。
我正在使用 unordered_maps 的 unordered_map,这样我就可以使用“多键”语法来引用元素: my_map[k1][k2]。 有没有一种方便的方法可以在尝试访问之前使用相同的“多
假设我有一个 unordered_map 定义如下: unordered_map> f_table; f_table[1][3] = 10; f_table[1][2] = 1; f_table[1]
我正在 interviewbit.com 上解决竞争性编程问题我基本上使用 unordered_map 来跟踪访问过的数字。当我使用 operator[] 时,我的代码无法及时执行,但是当我使用 fi
我有一张 map ,如下所示。 struct B { int b1; int b2; int b3; }; struct A { B a1; B a2; }; unordered
我有以下数据结构问题?你能帮帮我吗?所以我的要求是在我将新数据项添加到此 map 时将此数据结构初始化为默认值。 我怎样才能有效地做到这一点? 对于我要添加的每个条目,我需要将 a1、a2、a3 设置
对于我的下一个任务,我需要使用一个非常大的散列;因为我有一个旧的编译器,所以我不能使用 C++0x std::unordered_map。理想情况下,我需要调用 reserve 为大量元素提前腾出空间
我不明白为什么这个简短示例中的第二个代码块无法正确编译。我的理解是 <> 中的第二个参数表示值,它不需要是唯一的。为什么第二个代码块抛出编译器错误,我需要做什么来补救它? // Unordered M
这段代码运行成功,结果为“Character Found”。 unordered_map mp; mp['a'] = 'b'; char b='b'; if(mp['a'] && mp['a'] ==
std::unordered_map::emplace和std::unordered_map::insert在C++中有什么区别? 最佳答案 unordered_map::insert 将键值对复制或
哪个更有效率?有什么好的基准吗? 最佳答案 C++11 的 std::unordered_map 规范类似于基于 tr1::unordered_map 的 boost::unordered_map。话
使用 gcc 4.8.1 和 libboost 1.53,根据我用于编译代码的优化级别,我得到了不同的结果。作为更大程序的一部分,函数 insertValues 对相同的 a、key 和 value
我正在尝试使用 boost::mulprecision 类型创建一个 STL(或 boost)unordered_map,例如cpp_int 但 gcc 在尝试将元素插入此容器后抛出错误。 #incl
我是一名优秀的程序员,十分优秀!