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

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

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

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

Q&A

解決済

2回答

1474閲覧

Java SE8 Silver ArrayListを拡張forで要素削除しても例外にならない理由

spring_learner

総合スコア48

Java

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

0グッド

1クリップ

投稿2018/02/08 03:08

編集2018/02/08 03:12

Java SE8 Silverを学習しています。
教材はインプレスの「徹底攻略」です。

問題で気になるものがあり、Eclipse4.6.3を使って動作をさせました。

Java

1import java.util.ArrayList; 2 3public class Practice { 4 5 public static void main(String[] args) { 6 ArrayList<String> list = new ArrayList<>(); 7 list.add("A"); 8 list.add("B"); 9 list.add("C"); 10 11 for(String str : list){ 12 if("B".equals(str)){ 13 list.remove(str); 14 }else{ 15 System.out.println(str); 16 } 17 } 18 } 19 20}

こちらのコードですが、list.remove(str)により要素が一つ分繰り上がることで「A」のみが表示される、が答えとなります。実際実行しても同じ結果になりました。

ここで疑問なのですが、拡張for文でひとつずつ要素を取り出し、removeを使って削除した場合、スレッドセーフでないArrayListでは例外がスローされるはずです。なぜ、このコードでは例外がスローされないのでしょうか。

条件式を("A".equals(str))や("C".equals(str))にした場合は、例外がスローされます。Bのときだけ例外にならず要素の削除が行われています。

本問題に関わりのある問題も含め、解説を何度も読み返しましたが解決しませんでした。
どうかご指導のほどよろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

すでに何度か出ている質問ですのでリンク貼ります
ConcurrentModificationExceptionが発生するパターンについて

投稿2018/02/08 03:35

swordone

総合スコア20651

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

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

KSwordOfHaste

2018/02/08 03:44

既に優れた回答がある問題は同じような回答をさけてリンクした方がよいですね! 冗長な回答かいてしまいました・・・
swordone

2018/02/08 04:29

これとほぼ同じコードの質問が、過去に4~5回は出てますね。 「Java ConcurrentModificationException」とここで検索すれば出てきます。
KSwordOfHaste

2018/02/08 04:50

なるほど検索しやすいキーワードですね! コメントありがとうございます。
swordone

2018/02/08 12:57

というか、こんな内部コード読まないと正確な答えがわからないような問題、どこもきちんと解説していないのでしょうか…?
KSwordOfHaste

2018/02/08 13:26

確かに・・・ 間違ったコードを示し「なぜNGなのか」を詳細に説明するのが肝心と思いますが、質問者さんがお使いの教材で「間違ったコードが例外を起こさない」ケースをわざわざ選んだとしたらそれに対する説明があってしかるべきと思えてきますよね・・・。実際にその教材を見てないのでなんともいえませんが。
退会済みユーザー

退会済みユーザー

2018/02/08 16:15

しかしな 「この例外は、そのような変更が許可されていない場合に、オブジェクトの同時変更を検出したメソッドによってスローされる可能性があります。」 とあるが実際は next で取得時に発生してるからなぁ
KSwordOfHaste

2018/02/08 16:21

オブジェクトの同時変更を検出したメソッドがnextメソッドと解釈すればAPIの説明は特に不自然ではない気がします。
退会済みユーザー

退会済みユーザー

2018/02/08 16:25

まあ next で検出できるなら hasNexgt でも検出できるから仕様上のバグなんだよねこれ
KSwordOfHaste

2018/02/08 16:29

そうでしょうか?いつも検出できるとは限りませんといっているわけで、hasNextで検出しそこねたとしてもそれは最適化の都合(実装の都合)であってバグではないと思いますよ。hasNextでチェックしてもかまわないと思いますが、どのみち同期なしのチェックなのですからhasNextでチェックするようにしてもどっちみち完全な検出は無理ですよね・・・。
退会済みユーザー

退会済みユーザー

2018/02/08 16:38

んだね。でもIterator側とList側で保持している size の相違を見極めるだけでも 不可解さが減少すると思いますよ
KSwordOfHaste

2018/02/08 16:42

なるほど。非同期な変更のバグ検出ではなくIteratorで操作中のコレクションを変更する点についてのチェックという意味ですね。確かにそういう意味ではチェックしてくれた方が親切ではありますね。
spring_learner

2018/02/08 17:15

もう数回でている質問だったのですね。 シルバーの疑問はこれのみだったのですが、goldにも進むので次回は先に検索をかけてみます。 またここでの会話も大変勉強になります。開発未経験なので一部ついていけてませんが。
退会済みユーザー

退会済みユーザー

2018/02/08 23:00

ちなみにゴールドには「可能性が高い」とかくそみそな問題がw
guest

0

ベストアンサー

スレッドセーフでないArrayListでは例外がスローされるはずです。

この認識は間違いといってよいと思います。APIドキュメントには以下が記述されています。

(A)

このクラスのiteratorおよびlistIteratorメソッドによって返されるイテレータは、フェイルファストです。イテレータの作成後に、イテレータ自体のremoveまたはaddメソッド以外の方法でリストが構造的に変更されると、イテレータはConcurrentModificationExceptionをスローします。このように、並行して変更が行われると、イテレータは、将来の予測できない時点において予測できない動作が発生する危険を回避するために、ただちにかつ手際よく例外をスローします。

(B)

通常、非同期の並行変更がある場合、確かな保証を行うことは不可能なので、イテレータのフェイルファストの動作を保証することはできません。フェイルファスト・イテレータは、ベスト・エフォート・ベースでConcurrentModificationExceptionをスローします。したがって、正確を期すためにこの例外に依存するプログラムを書くことは誤りです。イテレータのフェイルファストの動作はバグを検出するためにのみ使用すべきです。

本コードはスレッドセーフかどうかというより(A)の制約に違反したコードです。それゆえ「間違い」ではあります。しかし(B)で記述されているようにArrayListのIteratorは「間違った使い方をしている場合に常に例外を発することは保証していない」点に注意が必要です。正しいコードを書くことはプログラマー自身が責任を持たなくてはならずライブラリーがそれを検出する保証がないのです。(そうなっている理由は実行効率を損なわないためです。)

本コードでは"B"を削除していますが、拡張for文の次の要素を調べるためにIterator#hasNext
が呼び出されると、本来は"C"が残っているにもかかわらず"B"が削除されたためにサイズが変化し「これ以上の要素はない」と誤判断され、for文自体が完了してしまいます。結局のところライブラリーを正しく使わないとき例外が発生する保証がないので、結果は未定義(どうなるかわからない)と思った方がよいでしょう。

試しに"B"ではなく"A"を削除してみてください。その場合は"A"が削除された後、"B"ではなく"C"が残っていると誤判断されるところまでは同じですが、Iterator#nextが実行されたとき「Iterator生成後に変更が行われている」ことにIteratorがたまたま気づいてくれるので例外を発するのです。つまり「たまたま誤りに気付いてくれるケースに該当したから例外がおきた」だけです。

投稿2018/02/08 03:41

KSwordOfHaste

総合スコア18394

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

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

spring_learner

2018/02/08 17:05

非常に分かりやすい説明ありがとうございました。 例外が発生する保証がないということだったんですね。正しくはないけど例外が起きないケース、がまさか試験に出てくるものとは。 APIドキュメントを読み直してみます。 大変勉強になりました。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問