Arraylistでforeachループを使用しようとしていますが、それを使用するとエラーが発生しますが、通常のforループを使用すると完全に機能しますが、問題は何ですか?
コードはここにあります:
for (Pair p2 : R) {
if ((p2.getFirstElm() == p.getSecondElm()) && (p2.getFirstElm() != p2.getSecondElm()))
R.add(new Pair (p.getFirstElm(), p2.getSecondElm()));
else if ((p2.getSecondElm() == p.getFirstElm()) && (p2.getFirstElm() != p2.getSecondElm()))
R.add(new Pair (p2.getFirstElm(), p.getSecondElm()));
// else
// There are no transitive pairs in R.
}
これは機能しないループであり、これが機能するループです。
for (int i = 0; i < R.size(); i++) {
if ((R.get(i).getFirstElm() == p.getSecondElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm()))
R.add(new Pair (p.getFirstElm(), R.get(i).getSecondElm()));
else if ((R.get(i).getSecondElm() == p.getFirstElm()) && (R.get(i).getFirstElm() != R.get(i).getSecondElm()))
R.add(new Pair (R.get(i).getFirstElm(), p.getSecondElm()));
//else
// There are no transitive pairs in R.
}
foreachループを使用したときに発生するエラーは次のとおりです。
Exception in thread "main" Java.util.ConcurrentModificationException
at Java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at Java.util.AbstractList$Itr.next(Unknown Source)
at set.problem.fourth.PoSet.makeTransitive(PoSet.Java:145)
at set.problem.fourth.PoSet.addToR(PoSet.Java:87)
at set.problem.fourth.PoSetDriver.typicalTesting(PoSetDriver.Java:35)
at set.problem.fourth.PoSetDriver.main(PoSetDriver.Java:13)
Javaコレクションクラスはフェイルファストです。つまり、一部のスレッドがイテレータを使用してコレクションをトラバースしている間にコレクションが変更されると、
iterator.next()
がConcurrentModificationException
をスローします。この状況は、マルチスレッド環境とシングルスレッド環境の両方で発生する可能性があります。 - www.javacodegeeks.com
_for/each
_ループでList
を変更することはできません。これは、実装の詳細としてのIterator
の周りの構文上のシュガーです。 Iterator
を直接使用する場合のみ、.remove()
を安全に呼び出すことができます。
Iterator.removeは、反復中にコレクションを変更する唯一の安全な方法です。反復の進行中に基になるコレクションが他の方法で変更された場合の動作は規定されていません。 - Javaコレクションのチュートリアル
_for/each
_ループ内で.add()
を呼び出すと、内容が変更され、舞台裏で使用されるIterator
がこれを認識し、この例外をスローします。
より微妙な懸念は、2番目にリストする方法である.size()
が.add()
を実行するたびに増加するため、最終的に.add()
、これは入力データが何であるかに応じて無限ループを引き起こす可能性があります。これがあなたの望むことかどうかはわかりません。
ソリューション
別のArrayList
と.add()
を作成して、すべての新しいものを作成し、ループの後、元のArrayList
で.addAll()
を使用して結合します2つのリストを一緒に。これにより、何をしようとしているのかが明確になります。つまり、新しく追加したものをすべて追加するときに、それらをすべて処理する意図がない場合です。
単一の共有クラスを変更するのではなく、常にImmutable
コレクションクラスを使用して、新しいImmutable
コレクションクラスを構築します。これは基本的に私の2012年の回答で述べられていることですが、もっと明確にしたかったのです。
Guavaはこれを非常によくサポートしています。ImmutableList.copyOf()
を使用してデータを渡します。
Iterables.filter()
を使用して、アイテムを新しいImmutableList
にフィルターで除外します。可変の共有状態はなく、同時実行の問題はありません。
問題は、ループの最初の行でR.add()を実行していることです。
最初の状況では、arraylistに対して開いているイテレータがあります。追加を行ってからもう一度イテレートしようとすると、イテレータはデータ構造が自分の下で変更されたことに気づきます。
Forルックの場合、毎回新しい要素を取得するだけで、同時変更の問題はありませんが、要素を追加するとサイズが変化します。
問題を修正するには、おそらく一時的な場所に追加し、ループ後に追加するか、初期データのコピーを作成して元の場所に追加する必要があります。