web-dev-qa-db-ja.com

Iteratorのremoveメソッドが実際にオブジェクトを削除する方法

反復中にコレクションからオブジェクトを削除する最も安全な(そしておそらく唯一の安全な)方法は、最初にIteratorを取得し、ループを実行して必要に応じて削除することです。

_Iterator iter=Collection.iterator();
while(iter.hasNext()){
    Object o=iter.next()
    if(o.equals(what i'm looking for)){
        iter.remove();
    }
}
_

私が理解したいのですが、残念ながら詳細な技術的説明は見つかりませんでしたが、この削除がどのように実行されるか、
次の場合:

_for(Object o:myCollection().getObjects()){
    if(o.equals(what i'm looking for)){
        myCollection.remove(o);
    }
}
_

ConcurrentModificationExceptionをスローしますが、「技術的に」Iterator.remove()は何をしますか?オブジェクトを削除し、ループを中断し、ループを再開しますか?

私は公式文書で見る:

「現在の要素を削除します。next()の呼び出しが先行していないremove()を呼び出そうとすると、IllegalStateExceptionがスローされます。」

「現在の要素を削除する」部分は、「通常の」ループで発生するまったく同じ状況を考えさせます=>(等価テストを実行し、必要に応じて削除します)が、イテレーターループはConcurrentModification-safeですか?

26
JBoy

Iteratorがどのように要素を削除するかは、実装によって異なります。これは、コレクションごとに異なる場合があります。間違いなく、あなたがいるループを壊すことはありません。ArrayList反復子がどのように実装されているかを見てきたので、コードを次に示します。

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

したがって、同時変更をチェックし、パブリックArrayList removeメソッドを使用して要素を削除し、リスト変更のカウンターをインクリメントして、次の反復でConcurrentModificationExceptionがスローされないようにします。

18

反復処理中にリストを変更できない理由は、反復子がhasNext()およびnext()に対して何を返すかを知る必要があるためです。

これを行う方法は実装固有ですが、ArrayList/AbstractList/LinkedListなどのソースコードを見ることができます。

また、状況によっては、次のようなコードを代替として使用できることに注意してください。

List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
  if (condition){
    origList.remove(foo);
  }
}

ただし、コレクションをコピーする必要があり(浅いコピーのみ)、削除する要素を検索する必要があるため、このコードの実行速度はおそらく若干遅くなります。

また、イテレータを直接使用している場合、変数のスコープが制限されるため、whileループではなくforループを使用することをお勧めします。

for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}
20
Puce