タイトル: 拡張for文
SEOタイトル: Java 拡張 for 文 (for-each) 完全ガイド
| この記事の要点 |
|
拡張 for 文 (for-each) の基本構文
// 構文: for (要素の型 変数 : 反復対象)
int[] nums = {1, 2, 3, 4, 5};
for (int n : nums) {
System.out.println(n);
}
// List
List<String> names = List.of("Alice", "Bob", "Carol");
for (String name : names) {
System.out.println(name);
}
// Set
Set<Integer> ids = Set.of(1, 2, 3);
for (int id : ids) {
System.out.println(id);
}
通常 for 文との比較
| 項目 | 通常 for | 拡張 for |
|---|---|---|
| index 取得 | 可能 (i) | 不可 |
| 反復中の要素削除 | 可能 | 不可 (ConcurrentModificationException) |
| 逆順反復 | 可能 | 不可 (リストの reverse 必要) |
| 記述量 | 多い | 少ない |
| 可読性 | 普通 | 高い |
| 速度 | 同等 | 同等 (コンパイル後ほぼ同じ) |
// 通常 for (index が必要なとき)
for (int i = 0; i < names.size(); i++) {
System.out.println(i + ": " + names.get(i));
}
// 拡張 for (シンプルに反復)
for (String name : names) {
System.out.println(name);
}
// 拡張 for + index が欲しい場合は自分でカウンタ
int i = 0;
for (String name : names) {
System.out.println(i + ": " + name);
i++;
}
Iterable インターフェース
拡張 for 文は配列とjava.lang.Iterableを実装した任意のクラスで使えます。自作クラスを反復可能にするには Iterable を実装するだけで OK。
import java.util.Iterator;
// 自作 Iterable
public class Range implements Iterable<Integer> {
private final int start, end;
public Range(int start, int end) { this.start = start; this.end = end; }
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
int current = start;
public boolean hasNext() { return current < end; }
public Integer next() { return current++; }
};
}
}
// 使う
for (int i : new Range(1, 5)) {
System.out.println(i); // 1, 2, 3, 4
}
Map の反復
Map は Iterable を直接実装していないので、entrySet() / keySet() / values() を経由します。
Map<String, Integer> scores = Map.of("Alice", 90, "Bob", 80);
// 1. entrySet (★ 推奨: key と value 両方使う場合)
for (Map.Entry<String, Integer> e : scores.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue());
}
// 2. keySet (key のみ)
for (String key : scores.keySet()) {
System.out.println(key);
}
// 3. values (value のみ)
for (int v : scores.values()) {
System.out.println(v);
}
// 4. Java 8+ forEach + Lambda
scores.forEach((k, v) -> System.out.println(k + " = " + v));
break / continue は使える
for (String name : names) {
if (name.equals("Bob")) {
continue; // この回をスキップ
}
if (name.equals("END")) {
break; // ループ脱出
}
System.out.println(name);
}
// ラベル付き break で多重ループ脱出
outer:
for (int[] row : matrix) {
for (int n : row) {
if (n == 0) break outer; // ★ outer ループも脱出
}
}
反復中の要素削除は不可
拡張 for 文の中で Collection の要素を削除すると ConcurrentModificationException が出ます。Iterator を直接使うか、別の方法を取ります。
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
// NG ConcurrentModificationException
for (String s : list) {
if (s.equals("b")) list.remove(s);
}
// OK1: Iterator.remove
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) it.remove();
}
// OK2: Collection.removeIf (Java 8+)
list.removeIf(s -> s.equals("b"));
// OK3: Stream + collect で新 List
list = list.stream()
.filter(s -> !s.equals("b"))
.collect(Collectors.toList());
Java 8+ Stream.forEach との違い
List<Integer> nums = List.of(1, 2, 3, 4, 5);
// 拡張 for (順次)
for (int n : nums) {
System.out.println(n);
}
// Stream.forEach (順次)
nums.forEach(System.out::println);
// Stream の真価: フィルタ + マップ + 集計
int sum = nums.stream()
.filter(n -> n % 2 == 0) // 偶数のみ
.mapToInt(Integer::intValue)
.sum();
// ★ parallelStream で並列処理 (順序保証なし)
nums.parallelStream().forEach(System.out::println);
使い分けチートシート
| ケース | 推奨 |
|---|---|
| 単純に全要素を 1 回ずつ処理 | 拡張 for |
| index が欲しい | 通常 for |
| 反復中に要素を削除 | Iterator or removeIf |
| 逆順 | 通常 for (i 降順) or Collections.reverse |
| フィルタ + 集計 | Stream |
| 並列処理 | parallelStream |
| Map 反復 | entrySet() + 拡張 for or forEach |
FAQ
Q: 拡張 for と通常 for どちらが速い?
A: ArrayList では同等。LinkedList では拡張 for (Iterator) のほうが速い (通常 for で get(i) すると O(n) 検索が毎回走る)。
Q: 配列の中身を変更できる?
A: プリミティブ配列ならループ変数を変えても配列本体は変わらない。参照型なら参照先のフィールドは変更可能。要素差し替えには index ベース for を使う。
Q: 拡張 for で null チェックは?
A: 反復対象自体が null だと NPE。事前に if (list != null) や Objects.requireNonNullElse(list, List.of())。