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);
}
}
}
Not a SSCCE, but can you pick up the smell?
The
Iteratorfor theHashSetclass is a fail-fast iterator. From the documentation of theHashSetclass:Note the last sentence – the fact that you are catching a
ConcurrentModificationExceptionimplies that another thread is modifying the collection. The same Javadoc API page also states:I believe the references to the Javadoc are self explanatory in what ought to be done next.
Additionally, in your case, I do not see why you are not using the
ImmutableSet, instead of creating a HashSet on thetermsobject (which could possibly be modified in the interim; I cannot see the implementation of thegetTermsmethod, but I have a hunch that the underlying keyset is being modified). Creating a immutable set will allow the current thread to have it’s own defensive copy of the original key-set.Note, that although a
ConcurrentModificationExceptioncan be prevented by using a synchronized Set (as noted in the Java API documentation), it is a prerequisite that all threads access the synchronized collection and not the backing collection directly (which might be untrue in your case as theHashSetis probably created in one thread, while the underlying collection for theMultiMapis modified by other threads). The synchronized collection classes actually maintain an internal mutex for threads to acquire access to; since you cannot access the mutex directly from other threads (and it would be quite ridiculous to do so here), you ought to look at using a defensive copy of either the keyset or of the MultiMap itself using theunmodifiableMultimapmethod of theMultiMapsclass (you’ll need to return an unmodifiable MultiMap from the getTerms method). You could also investigate the necessity of returning a synchronized MultiMap, but then again, you’ll need to ensure that the mutex must be acquired by any thread to protect the underlying collection from concurrent modifications.Note, I have deliberately omitted mentioning the use of a thread-safe
HashSetfor the sole reason that I’m unsure of whether concurrent access to the actual collection will be ensured; it most likely will not be the case.Edit:
ConcurrentModificationExceptions thrown onIterator.nextin a single-threaded scenarioThis is with respect to the statement:
if(c.isSomething()) C.remove(c);that was introduced in the edited question.Invoking
Collection.removechanges the nature of the question, for it now becomes possible to haveConcurrentModificationExceptions thrown even in a single-threaded scenario.The possibility arises out of the use of the method itself, in conjunction with the use of the
Collection‘s iterator, in this case the variableitthat was initialized using the statement :Iterator<BooleanClause> it = C.iterator();.The
Iteratoritthat iterates overCollectionCstores state pertinent to the current state of theCollection. In this particular case (assuming a Sun/Oracle JRE), aKeyIterator(an internal inner class of theHashMapclass that is used by theHashSet) is used to iterate through theCollection. A particular characteristic of thisIteratoris that it tracks the number of structural modifications performed on theCollection(theHashMapin this case) via it’sIterator.removemethod.When you invoke
removeon theCollectiondirectly, and then follow it up with an invocation ofIterator.next, the iterator throws aConcurrentModificationException, asIterator.nextverifies whether any structural modifications of theCollectionhave occurred that theIteratoris unaware of. In this case,Collection.removecauses a structural modification, that is tracked by theCollection, but not by theIterator.To overcome this part of the problem, you must invoke
Iterator.removeand notCollection.remove, for this ensures that theIteratoris now aware of the modification to theCollection. TheIteratorin this case, will track the structural modification occurring through theremovemethod. Your code should therefore look like the following: