如何在删除元素时防止重新散列 std::unordered_map?
Posted
技术标签:
【中文标题】如何在删除元素时防止重新散列 std::unordered_map?【英文标题】:How do I prevent rehashing of an std::unordered_map while removing elements? 【发布时间】:2012-11-23 16:56:45 【问题描述】:我有一个 std::unordered_map,我将从迭代中删除元素。
auto itr = myMap.begin();
while (itr != myMap.end())
if (/* removal condition */)
itr = myMap.erase(itr);
else
++itr;
我想阻止地图执行任何昂贵的操作,直到我完成删除所有需要删除的元素。我有正当的担忧吗?我是否误解了内部存储的工作原理?
【问题讨论】:
【参考方案1】:erase
期间禁止无序容器重新散列:
[unord.req]/p14:
erase
成员只能使迭代器和对 被擦除的元素,并保持元素的相对顺序 没有被删除。
[unord.req]/p9:
重新散列使迭代器无效,更改元素之间的顺序,以及...
你的代码没有问题。
【讨论】:
我知道 4 年后我们正在研究这个问题,但我很高兴看到这个答案加入其中。再次查看文档,很明显最糟糕的复杂性不是来自潜在的重新散列,而是来自散列冲突。我认为这是正式的正确答案。 所以表只能增长。? 在erase
下,无序容器中的桶数永远不会减少。在rehash
下允许数字缩小,所有实现都会这样做。【参考方案2】:
我不确定它是否有效,我在文档中没有找到确认 - 但如果 unordered_map 根据经典哈希表数据结构重新散列,您可以 set the max_load_factor到一个非常高的值并在完成后将其重置为正常值(这将触发重新哈希)(或者如果您可以预测将删除多少元素,则将其重置为预测值)。
就经典哈希表而言,它应该可以工作,因为当大小小于1/max_load_factor
时会发生减小表时的重新哈希。
(不确定在 C++ 中是否如此,但我认为值得一试,因为它真的很容易实现)。
【讨论】:
【参考方案3】:据我所知,std::unordered_map
可以在erase(itr)
上重新散列:
C++11 表 103 -- 无序关联容器要求
a.erase(q)
删除指向的元素 通过
q
。返回值为 紧跟在q
之后的迭代器 在擦除之前。平均情况
O(1)
, 最差 案子O(a.size())
因此,您似乎确实有一个合理的担忧。至于解决它,我可以提出几个途径:
-
确保这是一个实际问题而不是假设问题。分析应用程序,查看 C++ 库的源代码等。
如果是实际问题,请考虑使用不同的容器或不同的算法。
考虑通过与每个元素关联的布尔标志简单地标记要删除的元素,并不时清除已删除的元素,从而摊销成本。
考虑按照 cmets 中 @amit 的建议对负载系数进行试验。尽管仍允许容器花费
O(a.size())
时间来擦除元素,但不同的负载因子可能会影响应用程序的实际性能。
【讨论】:
虽然信息丰富且相关 - 它没有回答问题:How do I prevent rehashing of an std::unordered_map while removing elements?
@amit:如果您在字里行间阅读,确实如此(确切问题的答案是您不能:))
@amit:嗯,最坏的情况是O(a.size())
。它不依赖于其他任何东西,包括负载因子。
@NPE:最坏的情况是基于非常糟糕的哈希值,而不是重新哈希。所有 unordered_* 操作都有可能此时容器中的所有对象碰巧具有相同的哈希值。我几乎可以肯定 .erase 目前被禁止重新散列。
这个答案不正确。我已经添加了一个正确的答案。以上是关于如何在删除元素时防止重新散列 std::unordered_map?的主要内容,如果未能解决你的问题,请参考以下文章
Java HashMap 内部数据结构在重新散列期间如何变化?
在返回常量哈希码的情况下,Java8 Hashmap 重新散列