拡張 for 文 / for-each(Java)
コレクションや配列の全要素を順に処理するための構文です。『Java』の拡張for文(for-each文)は、インデックス変数を書かずに「要素を1つずつ取り出して処理する」という意図を簡潔に表現できます。内部的には Iterable インターフェースを使って動作しており、配列・ArrayList・HashSet など Iterable を実装したコレクションであれば同じ構文で走査できます。インデックスが必要な場合の対処法(IntStream.range() や従来の for 文への切り替え)も合わせて解説します。
構文
// 拡張for文(for-each文)の基本構文です
for (要素の型 変数名 : 配列またはコレクション) {
// 変数名 に各要素が順に代入されて処理されます
}
// 配列に使う例です
for (String item : 配列) {
System.out.println(item);
}
// ArrayList に使う例です(Iterable を実装しているため同じ構文で書けます)
for (String item : リスト) {
System.out.println(item);
}
// インデックスも必要な場合は IntStream.range() を使います(Java 8+)
IntStream.range(0, リスト.size()).forEach(i -> {
System.out.println(i + ": " + リスト.get(i));
});
// または従来のfor文に切り替えます
for (int i = 0; i < 配列.length; i++) {
System.out.println(i + ": " + 配列[i]);
}
拡張for文の特徴
| 項目 | 説明 |
|---|---|
| 対象 | 配列、および Iterable<T> を実装したコレクション(ArrayList・HashSet・LinkedList など)に使えます。 |
| 走査の向き | 先頭から末尾への一方向のみです。逆順に走査したい場合は従来の for 文を使います。 |
| インデックス | 取得できません。インデックスが必要な場合は IntStream.range() か従来の for 文を使います。 |
| 要素の変更 | ループ変数に再代入しても元の配列・コレクションには反映されません。要素を書き換えるには従来の for 文を使います。 |
| ループ中の削除 | 拡張 for 文中に remove() でコレクションを変更すると ConcurrentModificationException が発生します。削除には Iterator や removeIf() を使います。 |
サンプルコード
ForEachArray.java
public class ForEachArray {
public static void main(String[] args) {
// --- 配列への拡張for文 ---
// KOFの参戦ファイターを配列で管理します
String[] fighters = {"草薙京", "八神庵", "テリー・ボガード", "不知火舞", "K'"};
// 拡張for文で先頭から順に1要素ずつ取り出します
// インデックス変数 i を書かなくてよいため、意図がシンプルに伝わります
System.out.println("=== 参戦ファイター一覧 ===");
for (String name : fighters) {
System.out.println("・" + name);
}
// --- int配列への拡張for文 ---
// 各ファイターの戦闘力です
int[] power = {1800, 1700, 1600, 1500, 1900};
int total = 0;
for (int p : power) {
total += p; // 合計を積算します
}
System.out.println("合計戦闘力: " + total);
System.out.println("平均戦闘力: " + (total / power.length));
}
}
javac ForEachArray.java java ForEachArray === 参戦ファイター一覧 === ・草薙京 ・八神庵 ・テリー・ボガード ・不知火舞 ・K' 合計戦闘力: 8500 平均戦闘力: 1700
ForEachCollection.java
import java.util.ArrayList;
import java.util.HashSet;
public class ForEachCollection {
public static void main(String[] args) {
// --- ArrayList への拡張for文 ---
// KOFのチームメンバーをリストで管理します
ArrayList<String> teamJapan = new ArrayList<>();
teamJapan.add("草薙京");
teamJapan.add("二階堂紅丸");
teamJapan.add("大門五郎");
// ArrayList も Iterable を実装しているため、配列と同じ構文で走査できます
System.out.println("=== チーム日本 ===");
for (String member : teamJapan) {
System.out.println("メンバー: " + member);
}
// --- HashSet への拡張for文 ---
// 参戦チームをセットで管理します(重複なし)
HashSet<String> teams = new HashSet<>();
teams.add("チーム日本");
teams.add("チームボガード");
teams.add("チームヤガミ");
teams.add("チーム日本"); // 重複は無視されます
// HashSet はイテレーション順が保証されませんが、拡張for文で走査できます
System.out.println("=== 参戦チーム一覧 ===");
for (String team : teams) {
System.out.println("・" + team);
}
}
}
javac ForEachCollection.java java ForEachCollection === チーム日本 === メンバー: 草薙京 メンバー: 二階堂紅丸 メンバー: 大門五郎 === 参戦チーム一覧 === ・チームボガード ・チーム日本 ・チームヤガミ
HashSet のイテレーション順序は実行環境や JVM のバージョンによって異なります。上記の出力はあくまで一例であり、実際には別の順序になる場合があります。
ForEachWithIndex.java
import java.util.ArrayList;
import java.util.stream.IntStream;
public class ForEachWithIndex {
public static void main(String[] args) {
// KOFのランキングリストです
ArrayList<String> ranking = new ArrayList<>();
ranking.add("草薙京");
ranking.add("K'");
ranking.add("八神庵");
ranking.add("テリー・ボガード");
ranking.add("不知火舞");
// --- 方法1: IntStream.range() でインデックスを取得(Java 8+)---
// 拡張for文のシンプルさを保ちつつインデックスも使えます
System.out.println("=== ランキング(IntStream.range)===");
IntStream.range(0, ranking.size()).forEach(i -> {
System.out.println((i + 1) + "位: " + ranking.get(i));
});
// --- 方法2: 従来のfor文(インデックスが必要な場合の基本)---
// 逆順に出力したい、ステップを変えたいなど、柔軟な走査が必要な場合はこちらです
System.out.println("=== ランキング(逆順・従来for文)===");
for (int i = ranking.size() - 1; i >= 0; i--) {
System.out.println((i + 1) + "位: " + ranking.get(i));
}
// --- 拡張for文でのループ変数への再代入は元のリストに反映されない ---
// ループ変数 name は要素のコピーです。再代入しても ranking は変わりません
System.out.println("=== 再代入を試みても元のリストは変わらない ===");
for (String name : ranking) {
name = "テスト"; // ranking には影響しません
}
System.out.println("ranking の先頭: " + ranking.get(0)); // 草薙京のまま
}
}
javac ForEachWithIndex.java java ForEachWithIndex === ランキング(IntStream.range)=== 1位: 草薙京 2位: K' 3位: 八神庵 4位: テリー・ボガード 5位: 不知火舞 === ランキング(逆順・従来for文)=== 5位: 不知火舞 4位: テリー・ボガード 3位: 八神庵 2位: K' 1位: 草薙京 ranking の先頭: 草薙京
ForEachIterable.java
import java.util.Iterator;
import java.util.ArrayList;
public class ForEachIterable {
public static void main(String[] args) {
// --- Iterable の仕組みを確認する ---
// 拡張for文は内部的に Iterator を使ってコレクションを走査しています
// 次のコードは拡張for文と等価です
ArrayList<String> fighters = new ArrayList<>();
fighters.add("草薙京");
fighters.add("八神庵");
fighters.add("テリー・ボガード");
// 拡張for文(シンプルな書き方)
System.out.println("=== 拡張for文 ===");
for (String name : fighters) {
System.out.println(name);
}
// 上記と等価な Iterator を使った書き方(拡張for文がコンパイル後にこう展開されます)
System.out.println("=== Iterator(拡張for文の展開後イメージ)===");
Iterator<String> it = fighters.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}
// --- ループ中に要素を削除する場合は Iterator の remove() を使う ---
// 拡張for文の中で fighters.remove() を呼ぶと ConcurrentModificationException が発生します
// 安全に削除するには Iterator か removeIf() を使います
System.out.println("=== Iterator で要素を削除(八神庵を除外)===");
Iterator<String> it2 = fighters.iterator();
while (it2.hasNext()) {
String name = it2.next();
if (name.equals("八神庵")) {
it2.remove(); // Iterator 経由なら安全に削除できます
}
}
System.out.println("削除後: " + fighters);
// removeIf() を使うとさらに簡潔に書けます(Java 8+)
fighters.removeIf(name -> name.equals("草薙京"));
System.out.println("removeIf後: " + fighters);
}
}
javac ForEachIterable.java java ForEachIterable === 拡張for文 === 草薙京 八神庵 テリー・ボガード === Iterator(拡張for文の展開後イメージ)=== 草薙京 八神庵 テリー・ボガード === Iterator で要素を削除(八神庵を除外)=== 削除後: [草薙京, テリー・ボガード] removeIf後: [テリー・ボガード]
走査方法の使い分け
| 状況 | よく使われる書き方 |
|---|---|
| 全要素を順に処理(インデックス不要) | 拡張 for 文(最もシンプル) |
| インデックスも一緒に使いたい | IntStream.range(0, size).forEach(i -> ...)(Java 8+) |
| 逆順・ステップ指定・途中変更が必要 | 従来の for 文 |
| ループ中に要素を削除したい | Iterator.remove() または removeIf() |
| 関数型スタイルで処理したい | stream().forEach()(Stream API) |
よくあるミス1: 拡張 for 文中に remove() を呼んで ConcurrentModificationException
拡張 for 文の内部では Iterator を使ってコレクションを走査しています。走査中に remove() でコレクションを直接変更すると、Iterator が変更を検知して ConcurrentModificationException が発生します。
ForEachRemoveNg.java
import java.util.ArrayList;
public class ForEachRemoveNg {
public static void main(String[] args) {
ArrayList<String> fighters = new ArrayList<>();
fighters.add("草薙京");
fighters.add("八神庵");
fighters.add("テリー・ボガード");
// 拡張for文の中でコレクションを変更すると例外が発生します
for (String name : fighters) {
if (name.equals("八神庵")) {
fighters.remove(name); // ConcurrentModificationException が発生します
}
}
}
}
javac ForEachRemoveNg.java java ForEachRemoveNg Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967) at ForEachRemoveNg.main(ForEachRemoveNg.java:12)
ループ中に要素を削除するには Iterator.remove() か removeIf()(Java 8+)を使います。
ForEachRemoveOk.java
import java.util.ArrayList;
import java.util.Iterator;
public class ForEachRemoveOk {
public static void main(String[] args) {
ArrayList<String> fighters = new ArrayList<>();
fighters.add("草薙京");
fighters.add("八神庵");
fighters.add("テリー・ボガード");
// Iterator.remove() を使えば安全に削除できます
Iterator<String> it = fighters.iterator();
while (it.hasNext()) {
if (it.next().equals("八神庵")) {
it.remove();
}
}
System.out.println("Iterator.remove() 後: " + fighters);
// Java 8+ では removeIf() がさらに簡潔です
fighters.removeIf(name -> name.equals("草薙京"));
System.out.println("removeIf() 後: " + fighters);
}
}
javac ForEachRemoveOk.java java ForEachRemoveOk Iterator.remove() 後: [草薙京, テリー・ボガード] removeIf() 後: [テリー・ボガード]
よくあるミス2: ループ変数への再代入で元のコレクションが変わると思い込む
拡張 for 文のループ変数は各要素の「コピー(参照のコピー)」です。ループ変数に別の値を再代入しても、元の配列・コレクションの内容は変わりません。
ForEachReassignNg.java
import java.util.ArrayList;
public class ForEachReassignNg {
public static void main(String[] args) {
ArrayList<String> fighters = new ArrayList<>();
fighters.add("草薙京");
fighters.add("八神庵");
fighters.add("テリー・ボガード");
// ループ変数に再代入してもリストは変わりません
for (String name : fighters) {
name = "K'"; // ループ変数への再代入はリストに影響しません
}
System.out.println(fighters); // 変わっていません
}
}
javac ForEachReassignNg.java java ForEachReassignNg [草薙京, 八神庵, テリー・ボガード]
元のリストの要素を書き換えるには、従来の for 文でインデックス経由に代入します。
ForEachReassignOk.java
import java.util.ArrayList;
public class ForEachReassignOk {
public static void main(String[] args) {
ArrayList<String> fighters = new ArrayList<>();
fighters.add("草薙京");
fighters.add("八神庵");
fighters.add("テリー・ボガード");
// set() でインデックスを指定して要素を置き換えます
for (int i = 0; i < fighters.size(); i++) {
if (fighters.get(i).equals("八神庵")) {
fighters.set(i, "K'");
}
}
System.out.println(fighters); // [草薙京, K', テリー・ボガード]
}
}
javac ForEachReassignOk.java java ForEachReassignOk [草薙京, K', テリー・ボガード]
概要
拡張 for 文(for-each文)は、配列や Iterable を実装したコレクションを走査するための構文です。インデックス変数を書かずに「全要素を取り出して処理する」という意図だけをシンプルに表現できるため、可読性が高まります。
内部的には java.lang.Iterable インターフェースの iterator() メソッドが呼ばれ、Iterator の hasNext() と next() を繰り返すコードにコンパイル時展開されます。そのため、Iterable<T> を実装した独自クラスにも拡張 for 文が使えます。
ループ変数に再代入しても元の配列・コレクションの内容は変わりません。要素を書き換えたい場合は従来の for 文で直接インデックス経由に代入してください。また、ループ中にコレクションの要素を remove() で削除すると ConcurrentModificationException が発生します。削除には Iterator.remove() か Collection.removeIf() を使ってください。
インデックスが不要な全件走査は拡張 for 文、インデックスが必要な場合は従来の for 文か IntStream.range()、関数型スタイルで処理したい場合は stream().forEach() を選ぶとコードの意図が明確になります。for 文の基本については『for文』、Stream API の forEach() については『Stream forEach / reduce』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。