【问题标题】:HashSet Iterating While Removing Items in C#HashSet 在 C# 中删除项目时迭代
【发布时间】:2010-12-01 17:16:41
【问题描述】:

我在 C# 中有一个哈希集,如果在迭代哈希集时满足条件,我将删除该哈希集,并且无法使用如下的 foreach 循环执行此操作。

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

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

【问题讨论】:

    标签: c# hashset


    【解决方案1】:

    改用 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 代表什么?
    • 's' 表示正在评估的哈希集中的当前项目。查看更新的答案。
    • 这不会在 Java 中导致像 ConcurrentModificationException 这样的事情吗?我想知道 C# 是如何做到这一点的。
    • 我被锁定在一个赞成票中,但我应该反对这个答案,因为在foreach 循环中使用RemoveWhere 时它会导致System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
    • @PatPeter 答案是正确的,你只是用错了。迭代集合时不能更改集合;答案并不建议您这样做。它说使用RemoveWhere 没有 foreach 循环。
    【解决方案2】:

    您不能在使用枚举器循环遍历集合时从集合中删除项目。解决这个问题的两种方法是:

    • 使用常规索引 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);
    }
    

    【讨论】:

    • 该程序已经非常占用内存,所以我宁愿不使用另一个列表。谢谢
    • 我会避免使用两个 foreach 循环 - 一个 foreach 循环就足够了,看我的回答
    【解决方案3】:

    我会避免使用两个 foreach 循环 - 一个 foreach 循环就足够了:

    HashSet<string> anotherHashSet = new HashSet<string>();
    foreach (var item in hashSet)
    {
        if (!shouldBeRemoved)
        {
            anotherSet.Add(item);
        }
    }
    hashSet = anotherHashSet;
    

    【讨论】:

      【解决方案4】:

      通常当我想迭代某些东西并删除我使用的值时:

       For (index = last to first)
            If(ShouldRemove(index)) Then
                 Remove(index)
      

      【讨论】:

      • 谢谢,我知道我可以使用 for 循环,但您不能以这种方式使用索引位置访问 HashSet。如果我使用的是 C++,那么我会简单地使用指针放在我无法在 C# 中执行此操作。
      • 如果可能,您也可以考虑使用不同的数据结构。
      • 从 .Net 3.5 开始,您可以使用 ElementAt,在 Hashset 中,顺序无关紧要。
      【解决方案5】:

      对于那些正在寻找一种方法来处理 HashSet 中的元素同时删除它们的人,我是按照以下方式进行的

      var set = new HashSet<int> {1, 2, 3};
      
      while (set.Count > 0)
      {
        var element = set.FirstOrDefault();
        Process(element);
        set.Remove(element);
      }
      

      【讨论】:

      • 这只有在你想删除所有元素时才有效,但如果你只需要删除某些符合条件的元素(如 OP 状态),那么这将成为一个无限循环!如果您想要将它们全部处理并在处理后将其删除,为什么不直接执行foreach 循环来处理并在完成清理集合后?
      猜你喜欢
      • 2015-06-30
      • 2010-11-09
      • 2011-11-12
      • 1970-01-01
      • 1970-01-01
      • 2021-11-08
      • 2014-10-26
      相关资源
      最近更新 更多