反復中にコレクションからオブジェクトを削除する最も安全な(そしておそらく唯一の安全な)方法は、最初に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ですか?
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がスローされないようにします。
反復処理中にリストを変更できない理由は、反復子が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();){
...
}