【问题标题】:Java, multiple iterators on a set, removing proper subsets and ConcurrentModificationExceptionJava,集合上的多个迭代器,删除适当的子集和 ConcurrentModificationException
【发布时间】:2012-03-05 07:40:43
【问题描述】:

我有一个集合 A = {(1,2), (1,2,3), (2,3,4), (3,4), (1)}

我想把它变成 A={(1,2,3), (2,3,4)},从这个集合中删除适当的子集。

我使用 HashSet 实现集合,使用 2 个迭代器遍历集合并使用 containsAll(c) 检查所有对的正确子集条件,并使用 remove() 方法删除正确子集。

代码如下所示:

HashSet<Integer> hs....
Set<Integer> c=hs.values();
Iterator<Integer> it= c.iterator();
while(it.hasNext())
{
    p=it.next();
    Iterator<Integer> it2= c.iterator();
    while(it2.hasNext())
    {
        q=it2.next();
        if q is a subset of p
            it2.remove();
        else if p is a subset of q
        {
            it.remove();
            break;
        }
    }
}

我第一次退出内部 while 循环并执行一次时遇到 ConcurrentModificationException

p=it.next();

例外情况是在迭代集合时修改集合。但这就是 .remove() 的用途。

我在只使用 1 个迭代器时使用了 remove() 并且没有遇到任何问题。

如果异常是因为我在迭代它时从 'c' 或 'hs' 中删除了一个元素,那么当它遇到下一个它时应该抛出异常 2 .next () 命令,但我没有看到它。当它遇到 it.next() 命令时我看到了。

我使用了调试器,在元素被移除后,集合和迭代器的顺序非常完美。它们包含并指向正确的更新集和元素。 it.next() 包含要分析的下一个元素,它不是已删除的元素。

关于如何在不复制哈希集本身并在提交更新之前将其用作中间体的情况下如何做我想做的事情的任何想法?

谢谢

【问题讨论】:

    标签: java iterator subset


    【解决方案1】:

    我会写这样的……

    PriorityQueue<Set<Integer>> queue = new PriorityQueue<Set<Integer>>(16, 
     new Comparator<Set<Integer>>() {
      public int compare(Set<Integer> a, Set<Integer> b) {
        return b.size() - a.size(); // overflow-safe!
      }
    });
    queue.addAll(sets); // we'll extract them in order from largest to smallest
    List<Set<Integer>> result = new ArrayList<>();
    while(!queue.isEmpty()) {
      Set<Integer> largest = queue.poll();
      result.add(largest);
      Iterator<Set<Integer>> rest = queue.iterator();
      while(rest.hasNext()) {
        if(largest.containsAll(rest.next())) {
          rest.remove();
        }
      }
    }
    

    是的,它会消耗一些额外的内存,但它是惯用的、直接的,并且可能比其他方法更快。

    【讨论】:

      【解决方案2】:

      如何创建另一个包含您要删除的所有子集的子集 subsetNeedRemoved?对于每个子集,如果存在适当的超集,则将该子集添加到 subsetNeedRemoved。最后,您可以遍历subsetNeedRemoved,并删除原始集合中对应的子集。

      【讨论】:

        【解决方案3】:

        您不能使用it2 修改集合并继续使用it 对其进行迭代。正如异常所说,是并发修改,不支持。

        恐怕你被中间集合卡住了。

        编辑

        实际上,您的代码似乎没有意义:您确定它是Integer 的集合而不是Set&lt;Integer&gt; 的集合吗?在您的代码中,pqIntegers,因此“如果 q 是 p 的子集”似乎没有太大意义。

        让这变得更聪明的一个明显方法是:首先按大小对集合进行排序,从最大到最小,将要保留的集合添加到新列表中。您只需对照keep 列表检查每个集合,而不是整个原始集合。

        【讨论】:

        • 是的,你是对的。在实际代码中,它是Set,我只是在这里凭记忆写的。它实际上是Set&lt;Entry&lt;Integer, HashSet&lt;Integer&gt;&gt;&gt; 和其他一些东西,但基本的想法是我上面写的。我会修改问题。
        • Set&lt;Entry&lt;Integer, HashSet&lt;Integer&gt;&gt;&gt; 是一个有趣的结构,如果你不介意我问,那是为了什么?
        • 哈哈。对于我正在尝试做的事情,它可能有点低效。这就像一个图的邻接列表。条目的格式为 &lt;nodeX, HashSet&lt;adjacent nodes to nodeX&gt;&gt;
        【解决方案4】:

        ConcurrentModificationException 背后的想法是维护迭代器的内部状态。当您从一组项目中添加或删除事物时,即使没有出现任何错误,它也会抛出异常。这是为了使您免于编码错误,这些错误最终会在其他平凡的代码中抛出NullPointerException。除非您的空间限制非常有限或拥有非常大的集合,否则您应该只制作一个可以添加和删除的工作副本,而无需担心。

        【讨论】:

        • 现在,空间不是问题。但稍后,我将处理更大的数据集。按照上面的建议,维护一个单独的 keep 集可能是一个更好的选择,但我想绕过或解决这个问题,而不必求助于维护另一个集。
        • 那么你根本不能在同一个集合上同时使用 2 个迭代器,同时就地修改它。你甚至不能在修改它时使用它。如果您愿意,您可以找出大小并使用显式索引。届时您对循环的限制将发生变化,由您来跟踪。
        猜你喜欢
        • 1970-01-01
        • 2016-11-29
        • 2022-09-24
        • 2014-01-14
        • 2023-04-03
        • 2022-01-02
        • 1970-01-01
        • 2012-06-28
        • 2021-05-05
        相关资源
        最近更新 更多