Javaのフェイルファストとフェイルセーフイテレータの違いを徹底解説
Javaでコレクションを扱う際、反復処理(イテレーション)中にコレクションが変更された場合の挙動は非常に重要なポイントです。Javaのイテレータは、この挙動の違いによって「フェイルファスト(Fail-Fast)」と「フェイルセーフ(Fail-Safe)」の2種類に分類されます。本記事では、両者の違いを比較表と具体的なコード例を交えてわかりやすく解説します。
フェイルファストとフェイルセーフの比較表
| 項番 | 項目 | フェイルファスト(Fail-Fast) | フェイルセーフ(Fail-Safe) |
|---|---|---|---|
| 1 | 例外の挙動 | 反復処理中にコレクションへ要素の追加・削除・更新などの変更が加えられると、ConcurrentModificationException(並行変更例外)をスローします。 | 反復中にコレクションが変更されても例外をスローしません。 |
| 2 | 代表的なコレクション | ArrayList や HashMap などがフェイルファストイテレータの代表例です。 | CopyOnWriteArrayList や ConcurrentHashMap などがフェイルセーフイテレータの代表例です。 |
| 3 | パフォーマンスとメモリ | 実際のコレクションそのものに対して動作するため、余分なメモリや時間を必要としません。 | 実際のコレクションではなくその複製(クローン)に対して動作するため、時間とメモリのオーバーヘッドが発生します。 |
| 4 | 変更操作 | 反復処理中のコレクション変更は許可されません。 | 反復処理中でもコレクションの変更が許可されます。 |
フェイルファストとは?
フェイルファストイテレータは、反復処理中に元のコレクションが構造的に変更されると、即座に ConcurrentModificationException をスローして処理を中断します。これは、イテレータが内部的に保持している変更カウンタ(modCount)とコレクションの実際の変更回数を照合することで、変更を検知できる仕組みだからです。ArrayList、HashMap、LinkedList などの標準コレクションがこれに該当します。
フェイルセーフとは?
フェイルセーフイテレータは、元のコレクションのコピー(スナップショット)に対して反復処理を行うため、反復中に元のコレクションを変更しても例外は発生しません。ただし、コピーを作成する分だけメモリ消費と処理時間のコストがかかる点、そして反復中に行った変更が現在の反復結果には反映されない点には注意が必要です。CopyOnWriteArrayList や ConcurrentHashMap などがこれに該当します。
フェイルセーフのコード例
次の例では、ConcurrentHashMap(フェイルセーフ)を反復処理中に新しい要素を追加していますが、例外は発生せず正常に完了します。
public class FailSafeExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
// マップに要素を追加
map.put("Dell", 1);
map.put("IBM", 2);
// マップからイテレータを取得
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
System.out.println(key + " : " + map.get(key));
// 反復中に要素を追加しても例外は発生しない
map.put("Google", 3);
}
}
}
実行結果
IBM : 2 Dell : 1
フェイルファストのコード例
一方、次の例では ArrayList(フェイルファスト)を反復処理中に要素を追加しているため、ConcurrentModificationException がスローされます。
public class FailFastExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
// リストからイテレータを取得
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer integer = (Integer) it.next();
// 反復中に要素を追加すると例外が発生する
list.add(4);
}
}
}
実行結果
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
まとめ
フェイルファストは高速かつ省メモリである反面、反復中の変更に対して例外で即座に失敗する方式です。一方、フェイルセーフはオーバーヘッドと引き換えに、反復中の変更を安全に受け入れる方式です。マルチスレッド環境や反復中の変更が必要な场景ではフェイルセーフ系コレクション(java.util.concurrent パッケージ)を選択し、単一スレッドで変更を行わない場合は標準のフェイルファストコレクションを使うのが基本方針となります。用途に応じて適切に使い分けましょう。
-
JavaでのArrayListとHashSetの違いを徹底解説!使い分けのポイントも紹介
HashSetとArrayListは、どちらもJavaコレクションフレームワークにおいて最も重要なクラスの一つです。それぞれ特徴が異なるため、用途に応じた適切な使い分けが求められます。本記事では、ArrayListとHashSetの主な違いを6つの観点から比較し、実際のサンプルコードとともにわかりやすく解説します。ArrayListとHashSetの違い一覧No.項目ArrayListHashSet1実装インターフェースListインターフェースを実装しています。Setインターフェースを実装しています。2内部構造内部的には配列(動的配列)を使って実装されています。内部的にはHashMapを使って
-
JavaのIteratorとListIteratorの違いを徹底解説!特徴と使い分けのポイント
IteratorとListIteratorとは Javaでは、コレクションに格納された要素を1つずつ順番に取り出して処理するために、IteratorとListIteratorという2つのインターフェースが提供されています。 両者は内部実装こそ異なるものの、目的はどちらも「コレクション内のデータを順次走査する」ことです。しかし、利用できるコレクションの種類や、走査中に行える操作には大きな違いがあります。 IteratorとListIteratorの主な違い一覧 No.比較項目IteratorListIterator 1適用範囲List、Set、Queueなど、あらゆる種類のコレクションに対し