3

私は特定のタスクに取り組んでいましたが、偶然にも私によれば何か間違ったことをしましたが、コードが実行されて正しい結果が得られました。私は少し驚き、これらすべての for each ループがどのように機能するのか疑問に思っていました。例(サンプルプログラム)、

public static void main( String[] args )
{
    String myInput = "hello , hi , how are you ";
    String[] splitted = myInput.split(",");
    List<String> mylist = new ArrayList<String>();
    for (String output : splitted) 
    {
        mylist.add(output);
    }


    for (String output : mylist) 
    {
        System.out.println(output);
        mylist = new ArrayList<String>(); //It worked 
        mylist.add(output);
    }

    for (String output : splitted) 
    {
        mylist.add(output);
    }

    for (String output : mylist) 
    {
        System.out.println(output);             
        mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
    }

}

知りたかったのですが、検索中に、イテレータアプローチを使用するとリストから要素を削除できるという別の投稿を見つけたので、試してみました。

for (String output : splitted) 
{
    mylist.add(output);
}
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
    String string = (String) iterator2.next();
    System.out.println(string);
    iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}

上記で引用した every for each ループの背後で何が起こっているのかを知りたいだけです。
技術的な側面を知りたいのですが、 for each ループでコレクションを変更できないことはわかっていますが、上記のいくつかのケースでそれが機能したのはなぜですか?

4

5 に答える 5

3

上記で引用した every for each ループの背後で何が起こっているのか知りたいだけです

 1. for (String output : splitted) 
    {
        mylist.add(output);
    }

これにより、配列の各output文字列がリストに追加されます。splittedmylist

2. for (String output : mylist) 
{
      System.out.println(output);
      mylist = new ArrayList<String>(); //It worked 
      mylist.add(output);
}

このforステートメントは、次のプロダクションによって管理されています。

for ( FormalParameter : Expression )
            Statement

のインスタンス、または配列でExpressionなければなりません。java.lang.Iterableしたがって、このfor:eachループは次と同等です。

Iterator<String> iterator = mylist.iterator();
while (iterator.hasNext()) {
    System.out.println(output);
    mylist = new ArrayList<String>(); //It worked 
    mylist.add(output);
}

ここでは、型mylist.iterator()の新しいインスタンスを返しますIterator:

public Iterator<E> iterator() {
        return new Itr();
}

そのため、新しいArrayListインスタンスを作成mylistして反復ごとに割り当てた場合mylistでも、オリジナルから取得した反復子はオリジナルへの参照をmylist保持し、オリジナルの要素を繰り返し処理し続けますmylist。イテレータは、それが作成されたリストへの参照を保持します。代入は、イテレーター自体ではなくmylist = new ArrayList<String>()変数を変更するため、イテレーターが処理するデータには影響しません。mylistlist

3. for (String output : mylist) 
    {
        System.out.println(output);             
        mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
    }

以下のステートメントは、この動作を説明しています。Arraylistドキュメントからコピーされます:

このクラスの iterator および listIterator メソッドによって返される反復子はフェイルファストです。反復子の作成後にリストが構造的に変更された場合、反復子自体の remove メソッドまたは add メソッド以外の方法で、反復子は ConcurrentModificationException をスローします。したがって、同時変更に直面した場合、反復子は、将来の不確定な時点で恣意的で非決定論的な動作を危険にさらすのではなく、迅速かつ明確に失敗します。

4. for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
    String string = (String) iterator2.next();
    System.out.println(string);
    iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}

上記のステートメントは、この for ループの動作についても説明してlistいます。リストを繰り返し処理している間に、イテレータ自体の remove または add メソッドによって構造的に変更することができます。

于 2013-10-22T09:14:58.897 に答える
2

を実装するクラスでは、for-each ループが可能ですIterable。これは、for-each ループで使用できるクラスを自分で作成できることも意味します。これは非常に快適です。

iterator()このインターフェイスは、 を返すメソッドの実装を強制しますIteratorhasNext()次に、for-each ループは、その反復子を取得し、 andを使用して反復するだけnext()です。自分でやるのと同じです。

削除の問題は、for-each ループを使用してから List から要素を削除すると、構築Iteratorされた はその変更について何も認識せず、ConcurrentModificationException.

しかし、Iterator.remove()直接呼び出すと、Iterator はその変更を認識し、それを処理できます。

イテレータと例外を同時に回避するための一般的な小さなトリックは、次のようにすることです。

List<Object> objects = new ArrayList<Object>();
for (Object object : new ArrayList<Object>(objects)) {
    objects.remove(object);
}

したがって、そのリストの一時的なコピーを作成し、それを繰り返しますが、元のリストで remove を呼び出します。

于 2013-10-22T07:50:11.010 に答える
0

もちろん、次の行で行ったように、現在トラバースしているリストとはまったく異なるリストに何かを追加することは問題ではありませんmylist = new ArrayList<String>();

現在「ウォークスルー」されているリストに何かを追加できない理由は、そのリストの内部実装が保証できない可能性があるため、要素の同じ順序を取得し、特に残りのすべての要素を取得できないためです。あなたは期待するでしょう。これは、並べ替えられたリストを使用していると想像すると最もよく理解できます。新しい要素を入れますが、その要素が未定義であることがわかるかどうかは、現在の場所と何を挿入するかによって異なります。Java はそれで問題ないかどうかわからないため、安全な道をたどって Exception をスローします。

ただし、トラバーサル中に変更できるリストがあり、ほとんどが並行パッケージの並行リストです。

于 2013-10-22T08:38:23.170 に答える