繁体   English   中英

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

[英]Removing items from collection while iterating IEnumerable

我有以下代码:

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

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

谢谢。

不,这会炸弹或使您得到仍然有不良元素的结果。 只需反转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作战)或很小。 给定表达式,两者都不适用。

因为您正在遍历集合,所以它将引发错误。 枚举时不支持对集合的修改。

使用ToList()创建新列表

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

在对集合进行枚举时修改集合被认为是不好的做法,并且在许多情况下,这将导致引发InvalidOperationException

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

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

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

另一种选择是,可以切换到使用ConcurrentDictionary Dictionary不同, ConcurrentDictionary将允许删除项目而不会使正在进行的枚举无效。 如果只删除枚举的项目,我希望ConcurrentDictionary能够像预期的那样完全枚举。 如果枚举一个项目有时会导致代码删除另一个项目,则必须为以下事实准备代码:删除一个尚未枚举的项目可能但并非必须这样做 ,导致将该项目从枚举中省略。

尽管Dictionary通常比ConcurrentDictionary更快,但如果“ delete items where ...”操作很常见,并且必须删除或复制集合中的大部分项目,则最好使用后者。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM