来自Java 8中的TreeSet
(a SortedSet
):
::headSet
方法获取已排序集合前面的SortedSet
个对象。 ::removeAll
删除那些最前面的对象。ConcurrentModificationException
。然而,如果我从headSet创建另一个SortedSet并将该派生集传递给::removeAll
,那没问题。
为什么?
使用Java 8 Update 45演示代码。启用行//sortedSet.removeAll( headSet );
以查看抛出的异常。
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 );
答案 0 :(得分:3)
在第一种方法中,您在迭代它的同时修改集合(通过删除元素)(以获取要删除的元素)。抛出异常。请记住,头部集由原始集支持,因此修改原始集最终会修改头集 - 这是在迭代头集时无法做到的。
摘自TreeSet::headSet
doc(粗体强调是我的):
返回此集合部分的视图 .... 返回的集由此集支持,因此返回集中的更改将反映在此集中,反之亦然。
通常有两种方法来解决这个问题。一种是直接使用Iterator并使用其remove()
方法。另一种是通过首先迭代来创建集合的副本然后迭代副本以从原始中删除来分离迭代和修改;这就是你在第二种方法中所做的。