A recent talk about unordered_map in C++ made me realize that I should use unordered_map for most cases where I used map before, because of the efficiency of lookup ( amortized O(1) vs. O(log n) ). Most times I use a map, I use either int or std::string as the key type; hence, I've got no problems with the definition of the hash function. The more I thought about it, the more I came to realize that I can't find any reason of using a std::map over a std::unordered_map in the case of keys with simple types -- I took a look at the interfaces, and didn't find any significant differences that would impact my code.

因此,问题是:是否有任何真正的理由使用std::map而不是std::unordered_map在简单类型的情况下,如int和std::string?

我是从严格的编程角度提出这个问题的——我知道它并不是完全标准的,而且它可能会给移植带来问题。

另外,我认为正确答案之一可能是“对于较小的数据集更有效”,因为开销更小(是真的吗?)——因此,我希望将这个问题限制在键数量非普通的情况下(>1 024)。

编辑:哦,我忘记了显而易见的(感谢GMan!)——是的,地图当然是有序的——我知道,我正在寻找其他原因。


当前回答

通过使用std::unordered_map,您可以声明在代码中任何地方都不依赖于被排序的映射。在某些情况下,这些附加的上下文信息可能有助于理解这个映射在程序中是如何实际使用的。随着性能作为一个副作用的到来,清晰度可能更加重要。

当然,当您需要使用有序映射时,没有编译器会阻止您使用无序映射,但这不大可能工作得很好,因此读者可能会认为这不是一个错误。

其他回答

原因已在其他答案中给出;这是另一个。

std::map(平衡二叉树)操作平摊O(log n)和最坏情况O(log n)。 std::unordered_map(哈希表)操作平摊O(1)和最坏情况O(n)。

在实践中,哈希表每隔一段时间就会出现O(n)操作的“打嗝”,这可能是应用程序所能容忍的,也可能不是。如果它不能容忍,你更喜欢std::map而不是std::unordered_map。

我最近做了一个测试,做了50000个归并排序。这意味着如果字符串键是相同的,合并字节字符串。最终的输出应该是排序的。这包括查找每一个插入。

对于地图实现,完成这项工作需要200毫秒。对于unordered_map + map,插入unordered_map需要70 ms,插入map需要80 ms。所以混合实现快了50毫秒。

我们在使用地图之前应该三思。如果您只需要在程序的最终结果中对数据进行排序,那么混合解决方案可能会更好。

不要忘记map保持其元素的顺序。如果你不能放弃它,显然你不能使用unordered_map。

另外需要记住的一点是,unordered_map通常会使用更多的内存。Map只有一些内部指针和每个对象的内存。相反,unordered_map有一个大数组(在某些实现中会变得相当大),然后为每个对象提供额外的内存。如果需要内存感知,map应该会更好,因为它缺少大数组。

所以,如果你需要纯粹的查找-检索,我认为unordered_map是最好的方法。但总会有权衡,如果你负担不起,那你就不能使用它。

仅凭个人经验,我发现在主实体查找表中使用unordered_map而不是map时,性能有了巨大的改进(当然是度量的)。

另一方面,我发现它在重复插入和删除元素时要慢得多。它非常适合相对静态的元素集合,但如果您正在进行大量的插入和删除,那么哈希+桶似乎就会累加起来。(注意,这需要经过多次迭代。)

我只是想指出……有很多种unordered_map。

在哈希图上查找维基百科文章。根据所使用的实现的不同,查找、插入和删除方面的特征可能有很大差异。

这是我最担心的添加unordered_map到STL:他们将不得不选择一个特定的实现,因为我怀疑他们会走政策的道路,所以我们将被困在一个实现的平均使用,而没有其他情况…

例如,一些哈希映射具有线性重新哈希,其中不是一次重新哈希整个哈希映射,而是在每次插入时重新哈希一部分,这有助于分摊成本。

另一个例子:一些哈希映射使用一个简单的节点列表作为bucket,其他使用map,其他不使用节点,但找到最近的槽,最后一些将使用节点列表,但重新排序,以便最后访问的元素位于前面(像缓存一样)。

因此,目前我倾向于std::map或loki::AssocVector(用于冻结数据集)。

不要误解我的意思,我希望使用std::unordered_map,将来也可能会使用,但是当您想到实现它的所有方法和由此产生的各种性能时,很难“信任”这样一个容器的可移植性。

I was intrigued by the answer from @Jerry Coffin, which suggested that the ordered map would exhibit performance increases on long strings, after some experimentation (which can be downloaded from pastebin), I've found that this seems to hold true only for collections of random strings, when the map is initialised with a sorted dictionary (which contain words with considerable amounts of prefix-overlap), this rule breaks down, presumably because of the increased tree depth necessary to retrieve value. The results are shown below, the 1st number column is insert time, 2nd is fetch time.

g++ -g -O3 --std=c++0x   -c -o stdtests.o stdtests.cpp
g++ -o stdtests stdtests.o
gmurphy@interloper:HashTests$ ./stdtests
# 1st number column is insert time, 2nd is fetch time
 ** Integer Keys ** 
 unordered:      137      15
   ordered:      168      81
 ** Random String Keys ** 
 unordered:       55      50
   ordered:       33      31
 ** Real Words Keys ** 
 unordered:      278      76
   ordered:      516     298