final Multimap<Term, BooleanClause> terms = getTerms(bq);
for (Term t : terms.keySet()) {
Collection<BooleanClause> C = new HashSet(terms.get(t));
if (!C.isEmpty()) {
for (Iterator<BooleanClause> it = C.iterator(); it.hasNext();) {
BooleanClause c = it.next();
if(c.isSomething()) C.remove(c);
}
}
}
不是SSCCE,但你能闻到气味吗?
答案 0 :(得分:24)
答案 1 :(得分:8)
原因是您正在尝试在迭代器之外修改集合。
工作原理:
创建迭代器时,集合独立地为集合和迭代器维护一个modifyNum变量。 1.对于集合和迭代器的每次更改,收集变量都会递增。 2.对于迭代器的每次更改,迭代器的变量都会递增。
因此,当您通过迭代器调用it.remove()
时,会将modification-number-variable的值增加1.
但是当你直接在集合上调用collection.remove()
时,它只会增加集合的modification-number变量的值,而不会增加迭代器的变量值。
规则是:只要迭代器的modify-number值与原始集合的modify-number值不匹配,就会产生ConcurrentModificationException。
答案 2 :(得分:3)
Vineet Reynolds详细解释了集合抛出ConcurrentModificationException
(线程安全,并发)的原因。 Swagatika详细解释了该机制的实现细节(集合和迭代器如何保持修改次数)。
他们的回答很有趣,我对它们进行了投票。但是,在您的情况下,问题不是来自并发(您只有一个线程),而实现细节虽然有趣,但不应该在这里考虑。
您应该只考虑HashSet
javadoc的这一部分:
此类的迭代器方法返回的迭代器是快速失败的: 如果在创建迭代器后的任何时间修改了该集,则 除了通过迭代器自己的删除方法,Iterator之外的任何方式 抛出ConcurrentModificationException。因此,面对 并发修改,迭代器快速干净地失败, 而不是冒着任意的,非确定性的行为冒险 未来不确定的时间。
在你的代码中,你使用它的迭代器迭代你的HashSet,但你使用HashSet自己的remove方法来删除元素(C.remove(c)
),这会导致ConcurrentModificationException
。相反,正如javadoc中所解释的那样,您应该使用Iterator
自己的remove()
方法,该方法从基础集合中删除当前迭代的元素。
替换
if(c.isSomething()) C.remove(c);
与
if(c.isSomething()) it.remove();
如果您想使用功能更强大的方法,可以在HashSet
上创建Predicate并使用Guava的Iterables.removeIf()方法:
Predicate<BooleanClause> ignoredBooleanClausePredicate = ...;
Multimap<Term, BooleanClause> terms = getTerms(bq);
for (Term term : terms.keySet()) {
Collection<BooleanClause> booleanClauses = Sets.newHashSet(terms.get(term));
Iterables.removeIf(booleanClauses, ignoredBooleanClausePredicate);
}
PS:请注意,在这两种情况下,这只会从临时HashSet
中删除元素。 <{1}}将不会被修改。