我有一套A = {(1,2),(1,2,3),(2,3,4),(3,4),(1)}
我想把它变成A = {(1,2,3),(2,3,4)},从这个集合中删除正确的子集。
我正在使用HashSet来实现set,2迭代器来运行set并使用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();
例外是在迭代时修改Collection时。但这就是.remove()的用途。
我在使用1个迭代器时使用了remove(),并且没有遇到任何问题。
如果异常是因为我在迭代它时从'c'或'hs'中删除了一个元素,那么当它遇到下一个 2 时会抛出异常。 .next ()命令,但我没有看到它。我在遇到it.next()命令时看到它。
我使用了调试器,在删除元素后,集合和迭代器处于完美的顺序。它们包含并指向正确的更新集和元素。 it.next()包含要分析的下一个元素,它不是已删除的元素。
在我提交更新之前,如何在不制作哈希集本身副本并将其用作中间体的情况下如何做我想做的事情的任何想法?
谢谢
答案 0 :(得分:1)
您无法使用it2
修改集合,并继续使用it
进行迭代。正如例外所述,它是并发修改,并且不受支持。
我担心你会被中间收藏困住。
实际上,您的代码似乎没有意义:您确定它是Integer
而不是Set<Integer>
的集合吗?在您的代码中p
和q
是Integer
s,所以&#34;如果q是p&#34的子集;似乎没有多大意义。
一种明显的方法可以让它变得更聪明一些:首先按大小排序您的设置,当您从最大到最小时,将要保留的设置添加到新列表中。您只需要针对keep
列表检查每个集合,而不是整个原始集合。
答案 1 :(得分:0)
ConcurrentModificationException
背后的想法是维护迭代器的内部状态。当您添加或删除一组项目中的内容时,即使没有出现任何错误,它也会引发异常。这样可以避免编码错误,最终会在其他平凡的代码中抛出NullPointerException
。除非您有非常严格的空间限制或拥有非常大的集合,否则您应该制作一个可以无需担心地添加和删除的工作副本。
答案 2 :(得分:0)
如何创建包含要删除的所有子集的另一个set subsetNeedRemoved?对于每个子集,如果存在正确的超集,则将子集添加到subsetNeedRemoved。最后,您可以遍历subsetNeedRemoved并删除原始集中的相应子集。
答案 3 :(得分:0)
我会写这样的东西......
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();
}
}
}
是的,它消耗了一些额外的内存,但它是惯用的,直截了当的,并且可能比另一种方法更快。