迭代有序容器与无序容器

iterate ordered versus unordered containers

我想知道在 std::setstd::mapstd::unordered_setstd::unordered_map.[=19= 之间迭代其元素时哪些数据结构更有效]

我搜索了 SO 并找到了这个 question。答案建议复制 std::vector 中的元素或使用 Boost.Container,恕我直言,这不会回答我的问题。

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

结合使用

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

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

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

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

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

让我们考虑 setunordered_set

这里的主要区别是迭代的 'nature',即遍历集合会给你有序的元素,而遍历无序集合中的范围会给你一堆值没有特别的顺序。

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

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

集合的实现方式通常类似于红黑树(二叉搜索树的一种形式)。像所有二叉搜索树一样,允许对其元素进行高效的有序遍历(LRR:左根右)。那就是遍历你付出指针追逐的代价(就像遍历一个列表)。

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

在任何情况下,微调和基准测试都会为您的特定应用程序提供答案。

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