在迭代IEnumerable时从集合中删除项目

时间:2014-04-19 14:05:03

标签: c# list iteration ienumerable

我有以下代码:

foreach (var bar in dataFromDataFeed.Where(bar => bar.Key < fromTicks
                                                  || bar.Key > toTicks))
{
   dataFromDataFeed.Remove(bar.Key);
}

这是安全还是我需要先将foreach内的IEnumerable转换为Dictionary<T,U>

感谢。

4 个答案:

答案 0 :(得分:5)

不,那会炸弹或者让你得到一个仍然有坏元素的结果。只需反转Where表达式:

var filtered = dataFromDataFeed.Where(bar => bar.Key >= fromTicks && bar.Key <= toTicks);
dataFromFeed = filtered.ToList();   // optional

目前还不清楚你是否确实需要更新列表,因为你有一个非常好的枚举器,所以通常没有必要,所以最后一个语句是// optional

请记住,像在原始代码中一样使用Remove()会有O(n * m)复杂度,非常不好。使用ToList()仅为O(m)但需要O(m)存储。记忆的交易速度是一个常见的程序员的决定,但这是一个灌篮,除非m是巨大的(数亿和你正在与OOM战斗)或非常小。考虑到表达式,两者都不应该适用。

答案 1 :(得分:1)

由于您正在迭代集合,因此会抛出错误。在对其进行枚举时,不支持修改集合。

使用 ToList()

创建新列表
foreach (var bar in dataFromDataFeed.Where(bar => bar.Key < fromTicks
                                         || bar.Key > toTicks).ToList())
{
   dataFromDataFeed.Remove(bar.Key);
}

答案 2 :(得分:1)

在枚举时修改集合被认为是不好的做法,在很多情况下,这会导致抛出InvalidOperationException

您应该将值复制到另一个列表或数组中(例如,在ToList()调用后调用Where()),然后您将不会修改原始数据。

foreach (var bar in dataFromDataFeed.Where(bar => bar.Key < fromTicks || bar.Key > toTicks).ToList())
{
    dataFromDataFeed.Remove(bar.Key);
}

答案 3 :(得分:1)

尽管Dictionary几乎肯定会包含一个方法,该方法对每个项目都有谓词函数并删除该谓词返回true的所有项目,但它并不是。因此,如果一个Dictionary包含一些与谓词匹配的项目而另一些项目不匹配,那么获取字典的唯一方法就是只包含不满足谓词的项目。构建满足谓词的所有项目的列表,然后从字典中删除列表中的所有项目,或者构建一个仅包含不满足谓词的项目的字典,并放弃原始项目以支持新的项目一。哪种方法更好将取决于要保留和丢弃的项目的相对数量。

作为替代方案,可以切换到使用ConcurrentDictionary。与Dictionary不同,ConcurrentDictionary将允许移除项目而不会使正在进行的任何枚举失效。如果只删除枚举的项目,我希望ConcurrentDictionary能够完全按照预期进行枚举。如果枚举一个项目有时会导致代码删除不同的项目,那么必须准备代码,以便删除尚未枚举的项目但不需要,导致该项目从枚举中省略。

虽然Dictionary通常比ConcurrentDictionary更快,但如果&#34;删除项目,则使用后者可能是值得的...&#34;操作很常见,必须删除或复制集合中的大部分项目。