SortedSet :: removeAll (headSet) fails, but getting another collection from headSet succeeds. What for?
From TreeSet
(a SortedSet
) in Java 8:
- I am calling a method
::headSet
to getSortedSet
objects at the beginning of a sorted collection. - I call
::removeAll
to remove these very front objects.
BAM, issues .ConcurrentModificationException
However, if I make another SortedSet from headSet and pass the resulting derived set to ::removeAll
, no problem.
Why?
Demo code using Java 8 Update 45. Include the line //sortedSet.removeAll( headSet );
to see the exception thrown.
String cockatiel = "Cockatiel";
SortedSet sortedSet = new TreeSet< String >( );
sortedSet.add( "Dog" );
sortedSet.add( "Cat" );
sortedSet.add( "Bird" );
sortedSet.add( "Elephant" );
sortedSet.add( cockatiel ); // Passing var rather than literal.
sortedSet.add( "Guppy" );
System.out.println( "Before: " + sortedSet );
// Direct way. FAIL
SortedSet< String > headSet = sortedSet.headSet( cockatiel );
System.out.println( "headSet: " + headSet );
//sortedSet.removeAll( headSet ); // Fails. Throws java.util.ConcurrentModificationException.
// Derived way. PASS
SortedSet<String> headSetDerived = new TreeSet<String>( headSet); // Make a TreeSet from a TreeSet.
sortedSet.removeAll( headSetDerived ); // Succeeds. Why?
System.out.println( "After: " + sortedSet );
source to share
headSet with support for the original set
In the first method, you modify the set (by removing elements) while iterating over it (to remove elements). This throws an exception. Remember that the headset relies on the original set, so changing the original set ends up modifying the headset, which you can't do by iterating over that headset.
Excerpt from TreeSet::headSet
doc (bold is mine):
Returns a representation of a portion of this set .... The returned set is maintained by this set , so changes to the returned set are reflected in this set and vice versa.
Workarounds
There are generally two ways to get around this. One is to use the Iterator directly and use its method remove()
. Another is to separate the iteration and modification, first iterating to create a copy of the set, and then repeating the copy to remove from the original; this is what you did in your second approach.
source to share