在遍历集合时从集合中擦除的最有效方法
Most efficient way to erase from a set while iterating over it
在迭代时从集合中擦除最有效的方法是什么?以下是我想到的两种方法,哪一种是最好的?有没有更好的办法?
void WaitForFiles(std::set<string> files) {
while (files.size() > 0) {
std::set<string> found_files;
for (const auto& file : files) {
if (Exists(file)) {
found_files.insert(file);
}
}
for (const auto& found_file : found_files) {
files.erase(file);
}
}
}
使用set_difference: void WaitForFiles(std::set<string> files) {
while (files.size() > 0) {
std::set<string> found_files;
for (const auto& file : files) {
if (Exists(file)) {
found_files.insert(file);
}
}
std::set<string> difference;
std::set_difference(files.begin(), files.end(),
found_files.begin(), found_files.end(),
std::inserter(difference, difference.end()));
files = difference;
}
}
注意下面的崩溃:
void WaitForFiles(std::set<string> files) {
while (files.size() > 0) {
for (const auto& file : files) { // <-- seg fault after first erase
if (Exists(file)) {
files.erase(file);
}
}
}
}
为了确定效率,请记住,在我的例子中,文件可能需要30分钟才能存在,因此Exists函数将被调用多次,并且与循环迭代的次数相比,文件集不会经常更改。
在基于范围的for循环中从集合中擦除是未定义的行为(即使它看起来有效)。基于范围的for循环在内部使用迭代器,删除元素会使迭代器失效。
但是std::set::erase
返回std::set
中下一个元素的有效迭代器,因此您可以使用显式迭代器循环:
for(auto itr = files.cbegin(); itr != files.cend();) {
if (exists(*itr)) {
std::cout << "Found file: " << *itr << "n";
itr = files.erase(itr);
} else
++itr;
}
现场演示。
与std::experimental::erase_if
从v2的库基础TS:
std::experimental::erase_if(files, Exists);
如果Exists
是重载的或者是函数模板,使用lambda:
std::experimental::erase_if(files, [](const auto& f) { return Exists(f); });
您的前两个示例看起来不是最优的,因为它们都涉及遍历集合两次。第三个示例是不稳定的,因为在使用std::set::erase
更改集合使迭代器无效之后,您继续使用迭代器。
我认为下面的代码应该是相当有效的。然而,正如其他人提到的,用std::vector
代替std::set<std::string> found_files
可能是值得的。
#include <set>
#include <string>
#include <iostream>
/**
* Return true for every other call
*/
bool Exists(std::string const&)
{
static int i = 0;
return ++i % 2;
}
std::set<std::string> find_existing_files(std::set<std::string>& files)
{
std::set<std::string> found_files;
for(auto file = files.begin(); file != files.end();)
{
if(!Exists(*file))
++file; // not erasing, keep going
else
{
found_files.insert(*file);
// replacing iterator with result of erase()
// keeps the iterator valid
file = files.erase(file);
}
}
return found_files;
}
int main()
{
std::set<std::string> files {"a", "b", "c", "d", "e"};
for(auto const& file: find_existing_files(files))
std::cout << "exists : " << file << 'n';
for(auto const& file: files)
std::cout << "missing: " << file << 'n';
}
输出:exists : a
exists : c
exists : e
missing: b
missing: d
相关文章:
- 处理多个异常集合的C++方法
- 给定n个元素的m个集合.在C++中找到出现在最大集合数中的元素
- 欧拉项目#8答案是大以获得有效答案
- 如何将ampl中的集合表示为c++中的向量
- 调整大小后指向元素值的指针unordered_map有效?
- 为什么是0;C++中的有效语句
- 检查值是否在集合p1和p2中,但不在p3中
- find() 函数对集合有效吗?
- 如何更有效地从向量或集合中擦除元素?
- 将字符串保存在集合中时,C_STR()仍然有效
- 内存有效的加权设置分配,具有中等大小的集合
- 在 c++ 中搜索集合的最有效方法是什么
- 对基于地图C++集合重新排序的有效方法
- 迭代 Java 集合和对元素进行一些转换的有效方法
- 一个有索引的集合(用于在向量中有效地删除)
- 在遍历集合时从集合中擦除的最有效方法
- 在具有非关键字类型的集合中进行有效搜索
- c++ /JAVA:有效地在集合中找到包含给定值的集合
- 最有效的计算集合A对集合B的补的方法(向量)
- 集合和向量哪个更有效