質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

9952閲覧

拡張for文の動作がわかりません。

TosuTosu

総合スコア49

Java EE

Java EE(Java Enterprise Edition)はJavaベースのテクノロジーとその相互運用の仕様をまとめたものです。サーバとクライアントのアーキテクチャを規定し、特定アプリケーションのクラス用に定義されたテクノロジー設定のプロファイルを使用します。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2015/11/03 18:25

以下の拡張for文を利用したループ処理で①と③のコードで実行時エラーが発生します。
エラーが発生する場合と発生しない場合があり、拡張for文の動きがわかりません。
お知恵を貸していただきたい考え投稿させていただきました。

java

1 2 public static void main(String[] args) { 3 4 // ①のコード(実行時エラー) 5 ArrayList<String> list1 = new ArrayList<>(); 6 list1.add("A"); 7 list1.add("B"); 8 list1.add("C"); 9 for (String str : list1) { 10 if ("C".equals(str)) { 11 list1.remove(str); 12 }else { 13 System.out.println(str); 14 } 15 } 16 17 // ②のコード(正常終了) 18 ArrayList<String> list2 = new ArrayList<>(); 19 list2.add("A"); 20 list2.add("B"); 21 list2.add("C"); 22 list2.add("D"); 23 for (String str : list2) { 24 if ("C".equals(str)) { 25 list2.remove(str); 26 }else { 27 System.out.println(str); 28 } 29 } 30 // ③のコード(実行時エラー) 31 ArrayList<String> list3 = new ArrayList<>(); 32 list3.add("A"); 33 list3.add("B"); 34 list3.add("C"); 35 list3.add("D"); 36 list3.add("E"); 37 for (String str : list3) { 38 if ("C".equals(str)) { 39 list3.remove(str); 40 }else { 41 System.out.println(str); 42 } 43 } 44 } 45 46//実行結果 47①の結果 48 A 49 B 50 Exception in thread "main" java.util.ConcurrentModificationException 51 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 52 at java.util.ArrayList$Itr.next(ArrayList.java:851) 53 at Teratail.main(Teratail.java:11) 54②の結果 55 A 56 B 57③の結果 58 A 59 B 60 Exception in thread "main" java.util.ConcurrentModificationException 61 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 62 at java.util.ArrayList$Itr.next(ArrayList.java:851) 63 at Teratail.main(Teratail.java:40)

どうぞよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

参考情報を紹介します。

...
繰り返し処理中に要素を削除するようなこと自体が、言語の仕様を満たさない使用法
...

投稿2015/11/03 21:47

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

拡張for文には、配列かIterable実装クラスのオブジェクトが指定できます。ListはIterableを親に持つので、拡張for文に使用できます。後者の場合、内部ではIteratorが生成され、ループに使用されます。つまり、今回のコードは次と同じ意味になります。

java

1for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); ){ 2 String str = iterator.next(); 3 //処理 4}

(ただし、拡張for文の場合はこのiteratorにアクセスする術は存在しない)
この場合、for文の中で同じコレクションを参照するオブジェクトがListとIteratorの2つ存在する状態になります。
このように、Iteratorで反復処理をしている間は、Iterator自身の変更メソッド以外でリストに変更が加えられた場合(Listの方から変更を加えるような場合)にIteratorはできるだけ早く例外を発生させる仕組みになっています。
ArrayListがiterator()メソッドで生成するIterator(例外ログ中のArrayList$Itrがこれ)を調べてみると、hasNext()メソッドは「現在のカーソル位置の数値とリスト総数が同じでない場合trueを返す」という実装になっています。next()やremove()の操作の時に、もとのリストでIteratorが想定していない変更が加えられていた場合に、例外を発生させる仕組みになっています(例外ログのcheckForComodification)。
1と3のコードはリスト操作の結果、カーソル位置とリスト総数が一致しないためループが続行し、next()を呼んだ時に例外が発生しますが、2の場合はリスト操作の結果、たまたまカーソル位置とリスト総数が一致したため、次のループに入らずに、next()も呼ばれないために、例外が発生していないのです。

過去に同じような質問をしている方がいますのでこちらもご参照ください。
ConcurrentModificationExceptionが発生するパターンについて

投稿2015/11/03 23:59

編集2015/11/04 03:02
swordone

総合スコア20669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TosuTosu

2015/11/04 18:38

お返事が遅れ申し訳ありません。しっかり読ませていただきました。 少し時間がかかりました。 拡張for文を展開?で具体的なイメージがつきました。そうなるとコレクションを参照するオブジェクトはListとIteratorの2つで片方(List)に変更があると例外発生となるのですね。 他にもhasNext()、next()、remove()の挙動についても詳しく説明してくださり、今回の現象をつかむことができました。 やってはいけない操作とだけ考えていたので、深く理解することができました。 解答してくださった皆様方にもこちらの文章で恐縮ですがありがとうございます。 どうもありがとうございました!
guest

0

まず、実装修正から(①のみ)。

// ①のコード ArrayList<String> list1 = new ArrayList<>(); list1.add("A"); list1.add("B"); list1.add("C"); list1.remove("C"); for (String str : list1) { System.out.println(str); }

※list1.remove("C")は、list1内でCという要素を最初の1つのみ削除します。[リファレンス](https://docs.oracle.com/javase/jp/6/api/java/util/ArrayList.html#remove(java.lang.Object)
複数存在する可能性があれば、全て削除する実装が必要になります。

拡張for文の内部的な仕組みはわからないけど、
例外を調べてみると、

この例外は、オブジェクトの並行変更を検出したメソッドによって、そのような変更が許可されていない場合にスローされます。 たとえば、あるスレッドが Collection で繰り返し処理を行なっている間に、別のスレッドがその Collection を変更することは一般に許可されません。通常、そのような環境では、繰り返し処理の結果は保証されません。いくつかの反復子 (Iterator) の実装 (JRE が提供するすべての一般的な目的のコレクションの実装の、反復子の実装を含む) は、その動作が検出された場合にこの例外をスローすることを選択できます。この例外をスローする反復子は、「フェイルファスト」反復子と呼ばれます。 反復子は、将来の予測できない時点において予測できない動作が発生する危険を回避するために、ただちにかつ手際よく例外をスローします。 この例外は、オブジェクトが「別の」スレッドによって並行して更新されていないことを必ずしも示しているわけではありません。単一のスレッドが、オブジェクトの規約に違反する一連のメソッドを発行した場合、オブジェクトはこの例外をスローします。たとえば、フェイルファスト反復子を持つコレクションの繰り返し処理を行いながら、スレッドがコレクションを直接修正する場合、反復子はこの例外をスローします。 通常、非同期の並行変更がある場合、確かな保証を行うことは不可能なので、フェイルファストの動作を保証することはできません。フェイルファストオペレーションは最善努力原則に基づき、ConcurrentModificationException をスローします。したがって、正確を期すためにこの例外に依存するプログラムを書くことは誤りです。ConcurrentModificationException は、バグを検出するためにのみ使用してください。

とあります。
Javaは起動時に必ず1スレッド作られて、mainが実行されるので、
恐らくlist1.remove(str)の際に、別スレッドが立ち上がってるんじゃないかと思います。

②が正常終了できているという認識なようですが、Dが出力されていないのは、正常に終わってないです。
むしろ、例外が出ていないことが不気味です。

更に際しいことはこちらが参考になると思いますが、
つまるところ、for文のなかでfor文の条件要素数を変更することは止めたほうが良いです。

投稿2015/11/03 20:15

編集2015/11/03 20:29
TetsujiMiwa

総合スコア1124

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問