SortedSet :: removeAll(headSet)失败,但从headSet派生另一个集合成功。为什么?

时间:2015-07-13 04:50:18

标签: java exception concurrentmodification treeset sortedset

来自Java 8中的TreeSet(a SortedSet):

  1. 我调用::headSet方法获取已排序集合前面的SortedSet个对象。
  2. 我致电::removeAll删除那些最前面的对象。
    BAM,抛出ConcurrentModificationException
  3. 然而,如果我从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 );
    

1 个答案:

答案 0 :(得分:3)

由原始集合支持的headSet

在第一种方法中,您在迭代它的同时修改集合(通过删除元素)(以获取要删除的元素)。抛出异常。请记住,头部集由原始集支持,因此修改原始集最终会修改头集 - 这是在迭代头集时无法做到的。

摘自TreeSet::headSet doc(粗体强调是我的):

  

返回此集合部分的视图 .... 返回的集由此集支持,因此返回集中的更改将反映在此集中,反之亦然。

的变通方法

通常有两种方法来解决这个问题。一种是直接使用Iterator并使用其remove()方法。另一种是通过首先迭代来创建集合的副本然后迭代副本以从原始中删除来分离迭代和修改;这就是你在第二种方法中所做的。