SE17_C-01

Java

次のコードを見てください。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        strings.add("apple");
        strings.add("banana");
        strings.add("cherry");

        for (String s : strings) {
            System.out.print(s + " ");
            strings.remove(s);
        }
    }
}

このプログラムを実行すると、どのような出力が得られますか?また、その理由を説明してください。

選択肢

A) apple banana cherry
B) apple cherry
C) apple
D) 例外が発生する

答えと解説

A) apple banana cherry
B) apple cherry
C) apple
D) 例外が発生する

プログラムの流れ

  1. リストの初期化:javaコードをコピーするList<String> strings = new ArrayList<>(); strings.add("apple"); strings.add("banana"); strings.add("cherry"); この部分で、strings という ArrayList"apple", "banana", "cherry" の3つの文字列が追加されています。
  2. for-eachループの開始:javaコードをコピーするfor (String s : strings) { System.out.print(s + " "); strings.remove(s); } このループは、strings リスト内の各要素に対して反復処理を行います。

ループ内での要素削除

ループの中で strings.remove(s); を実行しています。これにより、現在処理している要素がリストから削除されます。これが問題の核心です。

ConcurrentModificationException

  • 構造の変更:
    • for-each ループは、リストの「構造」を変更することを許可しません。具体的には、要素を追加したり削除したりすると、イテレータが保持している状態と一致しなくなるため、ConcurrentModificationException が発生します。
  • イテレータの仕組み:
    • for-each ループは内部的にイテレータを使用しており、リストの構造が変更されるとイテレータはそれに対応できなくなります。この場合、Javaは安全性を確保するために例外をスローします。

例外の発生タイミング

  1. 最初のループで "apple" が処理されます。System.out.print(s + " "); が実行され、"apple " が出力されます。
  2. 次に strings.remove(s); により "apple" がリストから削除されます。この時点でリストの内容は ["banana", "cherry"] になります。
  3. 次に "banana" に進むと、イテレータはもはや削除された要素に対するアクセスができないため、ConcurrentModificationException が発生します。

まとめ

この問題は、コレクションを反復処理する際に注意が必要な点を示しています。要素を削除したり追加したりする必要がある場合は、以下の方法を考慮すべきです:

  • Iteratorを使う:javaコードをコピーするIterator<String> iterator = strings.iterator(); while (iterator.hasNext()) { String s = iterator.next(); System.out.print(s + " "); iterator.remove(); // Iteratorのremoveメソッドを使用 }
  • 別のコレクションにコピー:
    • 反復処理中に要素を削除する場合、別のリストに対象の要素を収集し、処理後に一括削除する方法もあります。

このように、コレクションの操作にはその特性を理解して行うことが重要です。

次の問題へ SE17_C-01