在堆中分配一个内存块。
是的 - 有一个“桶”数组的内存块,在 GCC 的情况下,它实际上是能够在前向链接列表中记录位置的迭代器。
对于每个 put 请求,对对象进行哈希处理并将其映射到此内存中的空间
不...当您将更多项目插入/放置到列表中时,会使用节点的next 链接空间和正在插入/放置的值完成额外的动态(即堆)分配。链表被相应地重新连接,因此新插入的元素链接到和/或来自散列到同一存储桶的其他元素,如果其他存储桶也有元素,则该组将链接到和/或来自节点那些元素。
在某些时候,哈希表内容可能如下所示(GCC 以这种方式做事,但可以做一些更简单的事情):
+-------> head
/ |
bucket# / #503
[0]----\/ |
[1] /\ /===> #1003
[2]===/==\====/ |
[3]--/ \ /==> #22
[4] \ / |
[5] \ / #7
[6] \ |
[7]=========/ \-----> #177
[8] |
[9] #100
-
左边的桶是原始分配的数组:图示数组中有 10 个元素,所以“bucket_count()”== 10。
-
具有哈希值 X 的键 - 表示为 #x,例如#177 - 散列到桶 X % bucket_count();该存储桶将需要将迭代器存储到单链表元素紧接在散列到该存储桶的第一个元素之前,因此它可以从存储桶中删除最后一个元素并重新连接头部或另一个存储桶的next 指针,跳过被擦除的元素。
-
虽然桶中的元素需要在前向链接列表中是连续的,但该列表中桶的顺序是容器中元素插入顺序的不重要结果,并且在标准中没有规定。
在此过程中,通过链接或开放寻址来处理冲突。
由哈希表支持的标准库容器始终使用单独的链接。
我很惊讶我找不到太多关于 unordered_map 如何处理内存的信息。 unordered_map 是否分配了特定的初始内存大小。
不,C++ 标准没有规定初始内存分配应该是什么;由 C++ 实现来选择。你可以通过打印出.bucket_count() 来查看一个新创建的表有多少个桶,如果你将它乘以指针大小,你很可能会得到无序容器所做的堆分配的大小:myUnorderedContainer.bucket_count() * sizeof(int*) .也就是说,您的标准库实现并没有禁止以任意和奇怪的方式(例如优化级别,取决于 Key 类型)更改初始 bucket_count(),但我无法想象为什么会有。
如果假设我们分配了 50 个 int 内存并且我们结束了会发生什么
插入 5000 个整数?这将是很多碰撞,所以我相信应该有一种类似于重新散列和调整大小的算法,以在达到一定水平的碰撞阈值后减少碰撞次数。
重新散列/调整大小不是由一定数量的冲突触发的,而是由一定的冲突倾向触发的,由 负载因子衡量,即.size() /.bucket_count().
当插入将.load_factor() 推到.max_load_factor() 之上时,您可以更改但C++ 标准要求默认为1.0,然后调整哈希表的大小。这实际上意味着它分配了更多的存储桶——通常接近但不一定是两倍多——然后它将新存储桶指向链表节点,最后删除旧存储桶的堆分配。
由于它们是作为成员函数显式提供给类的,因此我假设它们也在内部使用。有这样的机制吗?
没有关于如何实现大小调整的 C++ 标准要求。也就是说,如果我正在实现resize(),我会考虑创建一个函数本地容器,同时指定新需要的bucket_count,然后遍历*this 对象中的元素,调用extract() 将它们分离,然后merge() 将它们添加到函数本地容器对象中,然后最终在 *this 和函数本地容器上调用交换。