【问题标题】:iterate ordered versus unordered containers迭代有序容器和无序容器
【发布时间】:2015-07-21 08:33:30
【问题描述】:

我想知道在std::setstd::mapstd::unordered_setstd::unordered_map 之间迭代它们的元素时哪些数据结构更有效。

我搜索了 SO,发现了这个 question。答案要么建议复制std::vector 中的元素,要么使用Boost.Container,恕我直言不回答我的问题。

我的目的是在一个容器中保存大量独特的元素,大多数时候我都想遍历它们。插入和提取更为罕见。我想避免 std::vectorstd::unique 结合使用。

【问题讨论】:

  • 如果迭代频繁,你真的,真的,真的想要一个向量。
  • @101010:一般来说,这些问题不能通过思考来回答,只能通过经验测试来解决——而且答案可能会根据计算机本身的不同而变化,计算机上还运行着什么其他东西、库实现、容器的使用方式等等。
  • @101010:我数不清我读那本书的频率。 “我不能使用 Boost。”除非您的目标平台实际上不支持 Boost,否则我无法真正理解是谁提出了这些限制。 Boost 是标准库的下一个最好的东西,并且在原则上将它排除在项目之外是......好吧...... 在我在这里超越界限之前。 ;-)
  • @101010:我只想说,我希望你有足够的资格来讨论这个话题。至少让他们告诉你原因,因为不使用 C++ 的 Boost 有点像不使用 Java 中的设计模式,因为……未知。 ;-)
  • @Hurkyl '不能通过思考来回答'这将有助于肯定地思考

标签: c++ c++11 unordered-map unordered-set stdset


【解决方案1】:

让我们考虑 setunordered_set

这里的主要区别是迭代的“性质”,即集合的遍历会给你按顺序排列的元素,而在无序集合中遍历一个范围会给你一堆没有特定顺序的值。

假设您要遍历一个范围[it1, it2]。如果我们排除查找元素 it1 和 it2 所需的查找时间,则不会有从一种情况到另一种情况的直接映射,因为即使您使用相同的元素来构造容器,两者之间的元素也不能保证相同.

在某些情况下,这样的事情是有意义的,例如您想遍历固定数量的元素(无论它们是什么)或何时需要遍历整个容器。在这种情况下,您需要考虑实现机制

集合的实现通常类似于 红黑树(一种二叉搜索树)。像所有二叉搜索树一样,它们的元素允许有效的中序遍历(LRR:左根右)。也就是遍历你要付出追指针的代价(就像遍历一个列表一样)。

另一方面,无序集是哈希表,对于我的knowledge,STL 实现使用链式哈希。这意味着(在非常高的层次上)结构使用的是一个(连续的)缓冲区,其中每个元素都是包含元素的链(列表)的头部。元素在这些链(存储桶)和缓冲区中的布局方式将影响遍历时间,但是这次您将再次追逐指针以跳过不同的列表。我不认为它与树案例有很大不同,但肯定不会更好。

无论如何,微调和基准测试将为您的特定应用程序提供答案。

【讨论】:

  • link 添加到您之前编写的基准测试中......干杯。
【解决方案2】:

区别不在于订购或缺少一个,而在于支持容器。如果它是一个连续的内存,由于迭代器的简单实现和缓存友好性,它应该可以快速迭代。

无序容器通常存储为向量的向量(或类似的东西),而有序容器是使用树实现的,但它毕竟是留给实现的。这表明迭代无序版本应该是浪费。然而,这毕竟是要实现的,而且我看到了具有不同行为的实现(为了公平起见,它稍微扭曲了规则)。

一般来说,容器性能是一个相当复杂的话题,通常需要在实际应用中进行测试才能得到可靠的答案。有很多实现定义的东西可能会影响性能。如果我不得不盲人,我会选择hash_set。复制到vector 也可能是一个不错的选择。

编辑:正如@TonyD 在其评论中所说,有一条规则不允许在不超过max_load_factor() 时在添加元素期间使迭代器无效,这实际上排除了内存中连续的支持容器。

因此,将所有内容复制到向量中似乎是更合理的选择。如果您需要删除重复项,一个可行的选项可能是使用 http://en.cppreference.com/w/cpp/algorithm/sort 并且容易忽略重复项。我听说使用vectorsort 有一个排序数组(或向量)是一个经常使用的选项,以防需要一个需要排序的容器并且被迭代比修改更频繁。

【讨论】:

  • “无序容器通常存储为向量的向量(或类似的东西)” 仅当您认为链表向量相似时(我不):考虑到标准要求在插入过程中不移动现有对象且不会将负载因子增加到超过max_load_factor() 从而触发整个表重新散列,实际上可以保证不将连续的元素向量挂在桶外。尽管您提到“hash_set”,这是 C++11 之前实现的通用名称,但实现选择的余地并不像大多数人想象的那么多,而且它们各不相同....
  • @TonyD 我也不认为它们相似,根据我的第一段,记忆“连续性”在这里非常重要。我知道人们可以想到的移动空间更少,我想我曾经有过这样的(很棒的顺便说一句)讨论(我什至认为是和你一起讨论的),有一些微妙的规则基本上排除了一些实现。虽然,在某些情况下仍然足以影响性能。 IMO 它非常脆弱,确实需要测量。我将永远更新答案。复制到向量中可能是最好的选择。
【解决方案3】:

从最快到最慢的迭代应该是:set > map > unordered_set > unordered_map; set 比 map 轻一点,它们是按二叉树规则排序的,所以应该比 unordered_containers 快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-30
    • 2015-12-10
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多