在C#中遍历HashSet并删除其中的元素

34

我在C#中有一个哈希集合,如果在遍历哈希集合时满足条件,就会从哈希集合中删除元素,但不能使用下面的foreach循环来实现。

foreach (String hashVal in hashset) 
{
     if (hashVal == "somestring") 
     {
            hash.Remove("somestring");
     }
}

那么,我如何在迭代时删除元素呢?

6个回答

59
使用 HashSet 的 RemoveWhere 方法代替:

使用HashSet的RemoveWhere方法:

hashset.RemoveWhere(s => s == "somestring");

你需要将条件/谓词作为参数传递给该方法。与谓词匹配的哈希集中的任何项都将被移除。

这避免了在迭代哈希集时修改它的问题。


针对你的评论:

's' 表示当前正在从哈希集内部评估的项。

上述代码等同于:

hashset.RemoveWhere(delegate(string s) {return s == "somestring";});
或:
hashset.RemoveWhere(ShouldRemove);

public bool ShouldRemove(string s)
{
    return s == "somestring";
}

编辑: 我突然想到一个事情:由于HashSet是一个不包含重复值的集合,所以只需调用hashset.Remove("somestring")就足够了。不需要使用循环来执行,因为永远不会有多个匹配项。


谢谢,s代表什么? - aHunter
's'代表正在评估的哈希集中的当前项。请参见更新后的答案。 - adrianbanks
这不会在Java中引起任何像ConcurrentModificationException的问题吧?我想知道C#是如何处理的。 - PatPeter
我被困在一个赞同的状态中,但我本应该对这个答案进行反对,因为在使用RemoveWhere时,在foreach循环中会导致System.InvalidOperationException: Collection was modified; enumeration operation may not execute. - PatPeter
@PatPeter 答案是正确的,只是你使用方法不对。你不能在迭代中修改集合;答案也没有建议你这样做。它建议你使用“RemoveWhere”而不是“foreach”循环。 - Clonkex

10

在使用枚举器遍历集合时,不可以从其中删除项目。解决此问题的两种方法是:

  • 使用常规索引的for循环反向遍历集合(在HashSet的情况下可能不是一个选项)
  • 遍历集合,将要删除的项目添加到另一个集合中,然后遍历“待删除”集合并删除项目:

第二种方法的示例:

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("one");
hashSet.Add("two");

List<string> itemsToRemove = new List<string>();
foreach (var item in hashSet)
{
    if (item == "one")
    {
        itemsToRemove.Add(item);
    }
}

foreach (var item in itemsToRemove)
{
    hashSet.Remove(item);
}

程序已经相当占用内存,所以我宁愿不使用另一个列表。谢谢。 - aHunter
1
我会避免使用两个foreach循环 - 一个foreach循环就足够了,看看我的答案。 - Oleg Vazhnev
“在使用枚举器循环集合时,无法删除其中的项。”这个陈述现在已经不正确了,至少对于Core 3.0+中的字典而言是如此。尽管我不知道为什么HashSets尚未实现该功能。 - arkon

9
我建议避免使用两个foreach循环-一个foreach循环就足够了:
HashSet<string> anotherHashSet = new HashSet<string>();
foreach (var item in hashSet)
{
    if (!shouldBeRemoved)
    {
        anotherSet.Add(item);
    }
}
hashSet = anotherHashSet;

0

这里有一个更简单的解决方案。

var mySet = new HashSet<string>();
foreach(var val in mySet.ToArray() {
   Console.WriteLine(val);
   mySet.Remove(val);
}

.ToArray()已经为您创建了一个副本。您可以尽情循环。


0

对于那些正在寻找一种在移除HashSet中元素的同时处理它们的方法的人,我是这样做的

var set = new HashSet<int> {1, 2, 3};

while (set.Count > 0)
{
  var element = set.FirstOrDefault();
  Process(element);
  set.Remove(element);
}

这只适用于您想要删除所有元素的情况,但如果您需要仅删除与条件匹配的某些元素(如OP所述),则这将变成一个无限循环!如果您想要处理它们并在处理后将其删除,为什么不只需使用foreach循环进行处理,然后在完成后清除集合? - c-chavez

-1
通常当我想要迭代某些东西并删除值时,我会使用以下代码:
 For (index = last to first)
      If(ShouldRemove(index)) Then
           Remove(index)

谢谢,我知道我可以使用for循环,但是你不能以这种方式使用索引位置访问HashSet。如果我使用C ++,那么我将简单地使用指针,但我无法在C#中这样做。 - aHunter
如果可能的话,您也可以考虑使用不同的数据结构。 - Nescio
自 .Net 3.5 开始,您可以使用 ElementAt 方法,在哈希集中,顺序并不重要。 - c-chavez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接