在堆中分配一个内存块。
错误 - 这里有一个用于“bucket”数组的内存块,在GCC的情况下,它们实际上是迭代器,能够记录前向链接列表中的位置。
对于每个put请求,将对象进行哈希处理,并将其映射到该内存中的一个空间。
不是的...当你将更多项目插入/嵌入列表时,会进行额外的动态(即堆)分配,为节点的next链接和要插入/嵌入的值提供空间。 链接列表相应地进行了重连,因此新插入的元素与哈希到同一桶的其他元素链接,并且如果其他桶也有元素,则该组将链接到那些元素的节点。
在某些情况下,哈希表内容可能看起来像这样(GCC以这种方式处理事情,但可能做得更简单):
+-------> head
/ |
bucket
[0]----\/ |
[1] /\ /===>
[2]===/==\====/ |
[3]--/ \ /==>
[4] \ / |
[5] \ /
[6] \ |
[7]=========/ \----->
[8] |
[9]
左侧的桶是原始分配的数组:图示数组中有10个元素,因此 "bucket_count()" == 10。
哈希值为X的键 - 表示为#x,例如#177 - 哈希到桶X%bucket_count();该桶将需要存储一个迭代器,指向单链表元素,其紧挨着哈希到该桶的第一个元素之前,以便它可以从桶中删除最后一个元素,并重新连接头部或另一个桶的下一个指针,以跳过已擦除的元素。
虽然桶内的元素需要在前向链接列表中连续,但是桶的排序顺序不重要,这是容器中插入元素顺序的无关后果,并未在标准中规定。
在此过程中,通过链接或开放寻址处理冲突。标准库容器使用分离链接实现哈希表。
我很惊讶地发现关于unordered_map如何处理内存的资料不多。unordered_map分配的内存有特定的初始大小吗?
不,C++标准没有规定初始内存分配应该是什么,这由C++实现来选择。您可以通过打印.bucket_count()来查看新创建的表有多少个桶,很可能如果您将其乘以指针大小,您将得到无序容器所做的堆分配的大小:myUnorderedContainer.bucket_count() * sizeof(int*)。尽管如此,标准库实现对初始bucket_count()进行任意和奇怪的变化(例如,根据键类型进行优化级别),但我想象不出为什么会这样做。
如果我们分配了50个int内存,而我们最终插入了5000个整数会发生什么?这将导致很多冲突,因此我认为应该有一种重新哈希和重新调整大小的算法,在达到一定的冲突阈值后降低冲突数量。
重新散列/调整大小不是由一定数量的冲突触发的,而是由冲突易感性触发的,这是通过负载因子来衡量的,即
.size()
/
.bucket_count()
。
当插入操作将
.load_factor()
推高到
.max_load_factor()
以上时,哈希表会被调整大小。这实际上意味着它分配了更多的桶 - 通常接近但不一定完全是两倍 - 然后将新的桶指向链表节点,最后删除旧桶的堆分配。
由于它们明确地作为类的成员函数提供,我认为它们也会在内部使用。是否存在这样的机制?
关于如何实现重新调整大小,没有C++标准的要求。 话虽如此,如果我要实现resize()函数,我会考虑创建一个函数局部容器,同时指定新的bucket_count,然后遍历*this对象中的元素,调用extract()将它们分离,然后调用merge()将它们添加到函数局部容器对象中,最后调用swap()交换*this和函数局部容器。
unordered_map
存在问题的原因。Chandler Carruth有一段CppCon视频详细介绍了这个问题。如果我必须快速回忆起来,根据我尝试标准化flat_map
的经验,它可能与抛出移动操作、异常和强异常保证有关。 - Sean Middleditch