JavaでMapから条件付きで要素を削除する場合に、for文ではうまくいかない。これは、collectionの構造がiteration中に変更されて、ConcurrentModificationExceptionがthrowされたことによるものらしい。
代替手段として、removeIfメソッドを用いると想定通りにプログラムが動いた。ソースを見ると比較的スクリプトが少なく簡単に読めそうなので、詳細を確認しよう。
public interface Collection<E> extends Iterable<E> {
// 省略
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); //1
boolean removed = false; //2
final Iterator<E> each = iterator(); //3
while (each.hasNext()) { //4
if (filter.test(each.next())) { //5
each.remove(); //6
removed = true; //7
}
}
return removed; //1
}
/**
* Retains only the elements in this collection that are contained in the
* specified collection (optional operation). In other words, removes from
* this collection all of its elements that are not contained in the
* specified collection.
*
* @param c collection containing elements to be retained in this collection
* @return {@code true} if this collection changed as a result of the call
* @throws UnsupportedOperationException if the {@code retainAll} operation
* is not supported by this collection
* @throws ClassCastException if the types of one or more elements
* in this collection are incompatible with the specified
* collection
* ({@linkplain Collection##optional-restrictions optional})
* @throws NullPointerException if this collection contains one or more
* null elements and the specified collection does not permit null
* elements
* ({@linkplain Collection##optional-restrictions optional})
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
*/
①渡されたPredicateに対してのnullチェック
②最終的に削除が行われたかどうかの返り値の初期化
③削除をするためのcollectionのiteratorを取得。このeachに対して繰り返しの削除行為を実施する。
④iteratorに次の要素があるかを確認して、ある場合はループが継続
⑤条件に基づいてbooleanを決定
⑥該当する条件のものはiteratorから削除
⑦⑧1つでも削除したものがある場合は最終的にtrueを返す
重要なのはcollectionとiterator(iterable)の違いということのようだ。
- iterable:その名の通り、コレクションの要素を反復処理するためのインターフェースであり、特にfor-each(enhanced for statement)の対象となる
- collection:iterableを継承し、より多くの操作を可能としている
今回自分の中で腑に落ちていない点は以下の通り、今後勉強する中で解決することを望む。
- iteratorの変更がどうやって元のcollectionに反映されるのか
- removeIfの返値booleanはどこで使われているか
- nextまわりは記載がかなり省略されている感じがして肌に合わない