set与unordered_set进行最快迭代
set vs unordered_set for fastest iteration
在我的应用程序中,我有以下要求-
-
数据结构将只填充一次某些值(而不是键/值对(。这些值可能会重复,但我希望数据结构只存储一次。
-
我将在上面创建的数据结构的所有元素中迭代100次。元素在迭代中出现的顺序无关紧要。
约束1表明,由于数据不是键值对的形式,我将不得不使用set或undered_set。
现在,set插入的成本比unsodered_set插入高,但数据结构在程序开始时只填充一次。
我相信决定因素将是我在数据结构的所有元素中迭代的速度。我不确定set还是uncodered_set会更快。我相信标准没有提到这个事实,因为对于任何一个数据结构,这个操作都是O(n(。但我想知道哪种数据结构迭代器.next((会更快。
有几种方法。
- 对您的问题的评论建议保留一个具有最快
O(1)
查找/插入和O(N)
迭代的std::unordered_set
(就像每个容器一样(。如果您的数据变化很大,或者需要大量随机查找,这可能是最快的。但是测试 - 如果您需要在没有中间插入的情况下迭代100次,则可以将单个
O(N)
复制到std::vector
,并从连续内存布局中获得100次测试这是否比常规std::unordered_set
更快 - 如果在迭代之间有少量的中间插入,那么使用专用向量是值得的。如果您可以使用Boost.Container,请尝试
boost::flat_set
,它提供了一个带有std::vector
存储后端的std::set
接口(即一个对缓存和预取非常友好的连续内存布局(。再次,测试这是否会加速其他两个解决方案
对于最后一个解决方案,请参阅Boost文档中的一些权衡(最好注意所有其他问题,如迭代器无效、移动语义和异常安全(:
Boost.Container flat_[multi]映射/设置容器为有序向量基于Austern和Alexandrescu的关联容器指导方针这些有序的矢量容器也从中受益最近在C++中添加了移动语义,加快了速度插入和擦除时间相当长。平面关联容器具有以下属性:
- 比标准关联容器更快的查找
- 比标准关联容器更快的迭代
- 较小对象的内存消耗较少(如果使用了shrink_to_fit,则较大对象的内存占用较少(
- 提高了缓存性能(数据存储在连续内存中(
- 非稳定迭代器(插入和擦除元素时迭代器无效(
- 无法存储不可复制和不可移动的值类型
- 与标准关联容器相比,异常安全性较弱(复制/移动构造函数在擦除中移位值时可能抛出和插入(
- 插入和擦除速度比标准关联容器慢(特别适用于不可移动类型(
注意:使用更快的查找,意味着flat_set
在连续内存上执行O(log N)
,而不是O(log N)
指针追逐常规std::set
。当然,std::unordered_set
执行O(1)
查找,这对于大的N
将更快。
我建议您使用set或unordered_set进行"过滤",完成后,将数据移动到固定大小的向量
如果数据结构的构建没有考虑到性能问题(或至少只是轻微的(,请考虑将数据保存到std::vector
中:没有什么比更好的了
为了加快数据结构的初始构建,您可以首先插入到std::unordered_set
中,或者在插入之前至少使用一个来检查是否存在。
在第二种情况下,它不需要包含元素,但可以包含例如索引。
std::vector<T> v;
auto h = [&v](size_t i){return std::hash<T>()(v[i]);};
auto c = [&v](size_t a, size_t b){return v[a] == v[b];};
std::unordered_set<size_t, decltype(h), decltype(c)> tester(0, h, c);
无序集合使用哈希表来提供接近O(1(的时间搜索。这是通过使用键的哈希来计算您要查找的元素(键(与数据集开头的偏移量来完成的。除非数据集很小(如char
s(,否则不同的密钥可能具有相同的哈希(冲突(。
为了最大限度地减少冲突,无序集必须保持数据存储的稀疏性。这意味着找到一个密钥将是O(1(时间(除非发生冲突(。
然而,当迭代哈希表时,迭代器将在数据存储中遇到大量未使用的空间,这将减慢迭代器查找下一个元素的速度。我们可以用额外的指针链接哈希表中的相邻元素,但我不认为无序集能做到这一点
鉴于上述情况,我建议您使用排序向量作为"集合"。使用平分,您可以在O(logn(时间内搜索存储,并在列表中迭代是微不足道的。向量还有一个额外的优点,即内存是连续的,因此您不太可能遇到缓存未命中的情况。
我强烈建议您不要在这种情况下使用。set
是二叉树,unordered_set
是哈希表,因此它们占用大量内存,迭代速度慢,引用的局部性差。如果您必须频繁插入/删除/查找数据,set
或unordered_set
是不错的选择,但现在您只需要读取、存储、排序数据一次,只使用数据多次。
在这种情况下,排序向量可能是一个很好的选择。vector
是动态数组,因此开销较低。
直接查看代码。
std::vector<int> data;
int input;
for (int i = 0; i < 10; i++)
{
std::cin >> input;
data.push_back(input); // store data
}
std::sort(data.begin(), data.end()); // sort data
仅此而已。您的所有数据都已准备就绪。
如果您需要删除像set
这样的重复项,只需在排序后使用unique
-erase
即可。
data.erase(
std::unique(data.begin(), data.end()),
data.end()
);
请注意,您应该使用lower_bound
、upper_bound
和equal_range
,而不是find
或find_if
来使用排序数据的好处。
- 对于set上的循环-获取next元素迭代器
- 在 c++ 中定义一组 set 的迭代器
- 我从 std::set 得到const_iterator而不是迭代器
- 从 std::set 中删除元素,同时在 C++17 中迭代该元素
- 为什么 2 个未初始化的 std::set::迭代器相等?
- 使用 find() 通过 std::set 的迭代器将不起作用。出了什么问题?
- 使用set<set>迭代器的编译错误<int>
- STL SET 迭代器赋值 没有可行的候选者
- map/set迭代器不可递增,我无法从头开始重新启动映射
- std::set::迭代器和std::set::const_iterator之间是否存在操作差异
- std::set::迭代器的大小有限制吗
- std::set::迭代器转换错误
- C ++ map/set 迭代器不能使用 .find 取消引用
- std::set迭代顺序是否始终根据C++规范升序
- map/set迭代器不是可解引用的c++ map
- 对set迭代器解引用会导致seg错误
- 断言失败- map/set迭代器不兼容
- std::set迭代器的Const_pointer_cast
- C++Set迭代程序未到达Set的末尾
- Map /set迭代器不可解引用.多映射容器