Java | 基礎文法:for-each 文

Java Java
スポンサーリンク

for-each 文の全体像

for-each(拡張 for 文)は「配列やコレクションの全要素を順に取り出して処理する」ための最短記法です。書式は for (要素の型 変数 : 配列またはIterable) { ... }。インデックス管理が不要で、境界バグ(オフバイワン)を防ぎ、読みやすさが大幅に向上します。対象は配列と IterableList, Set など)で、マップは entrySet() などを使って要素化します。


基本構文と動き

最小の書式(配列)

String[] names = {"sato", "suzuki", "sakai"};
for (String n : names) {
    System.out.println(n);
}
Java

左側の型と変数は「各要素」を受け取ります。n は読み取り用の一時変数で、ループごとに要素が代入されます。インデックスは使いません。

コレクション(List/Set)

java.util.List<Integer> list = java.util.List.of(3, 8, 2);
for (int v : list) {
    System.out.println(v);
}
Java

Iterable を実装するコレクションなら同様に使えます。マップは「キーと値のペア」を要素化します。

java.util.Map<String, Integer> map = java.util.Map.of("A",1, "B",2);
for (java.util.Map.Entry<String, Integer> e : map.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}
Java

重要ポイントの深掘り:いつ使うか・使わないか

適材適所(向いている場面)

  • 全件処理: 全要素に同じ処理を行う。
  • 境界安全: インデックス不要でオフバイワンを防げる。
  • 読みやすさ重視: 意図(「これを順に処理」)が一目で伝わる。

不向きな場面(通常の for を選ぶ)

  • インデックスが必要: 位置に応じた分岐、先頭・末尾の区別、隣接要素参照。
  • 逆順処理: for-each は前からのみ。後ろから処理したいなら通常の for。
  • 途中で要素を安全に削除したい: コレクションでは Iterator.remove() が必要。for-each 中の list.remove(...)ConcurrentModificationException の原因になります。

重要ポイントの深掘り:変更と副作用

配列要素の「再代入」と「変数再代入」の違い

int[] a = {10, 20, 30};
for (int v : a) {
    v = v + 1; // v は「値のコピー」。配列は変わらない
}
System.out.println(java.util.Arrays.toString(a)); // [10, 20, 30]
Java

v を書き換えても配列は変わりません。要素を更新するなら添字が必要です。

for (int i = 0; i < a.length; i++) {
    a[i] = a[i] + 1;
}
Java

参照型の場合は「参照が渡される」ため、中身の状態を変更する操作は反映されます。

class Box { int value; }
Box[] boxes = { new Box(), new Box() };
for (Box b : boxes) {
    b.value++; // 参照先の状態を変更 → 配列から見ても反映される
}
Java

コレクションの安全な削除

java.util.List<Integer> list = new java.util.ArrayList<>(java.util.List.of(3, -1, 8));
for (java.util.Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
    int v = it.next();
    if (v < 0) it.remove(); // 安全
}
// for-each 中に list.remove(v) は危険(同時変更で例外)
Java

要素の追加も同様に注意が必要です。構造変更を伴う操作は、イテレータ方式か別の収集先を使います。


2次元・マップ・ストリームとの併用

2次元配列(配列の配列)

int[][] m = {{1,2,3},{4,5,6}};
for (int[] row : m) {
    for (int x : row) {
        System.out.printf("%3d", x);
    }
    System.out.println();
}
Java

外側で行、内側で各行の要素を処理します。ジャグ配列でも安全に走査できます。

マップの反復(キー、値、エントリ)

java.util.Map<String, Integer> map = java.util.Map.of("A",1, "B",2);
for (String k : map.keySet())   System.out.println("key=" + k);
for (Integer v : map.values())  System.out.println("val=" + v);
for (var e : map.entrySet())    System.out.println(e.getKey() + "=" + e.getValue());
Java

用途に応じて取り出すビューを選択します。

ストリームとの関係(参考)

「加工+集計」を一行で書きたいなら stream() も選択肢です。ただし、for-each は直感的で学習コストが低く、デバッグが容易という利点があります。


制御文(break/continue)と例外安全

break/continue の使い方

for (String s : java.util.List.of("OK:1", "BAD:x", "OK:2")) {
    if (!s.startsWith("OK:")) break;    // ここで終了
    System.out.println("process: " + s);
}
Java

continue は現在の周回をスキップして次へ進みます。通常の for と同様に使えます。

Null と型の一致

配列・コレクションに null が含まれる場合、メソッド呼び出し時に NullPointerException を避けるため、ガードを前に置きます。要素型は宣言側と一致させ、プリミティブ配列とラッパー配列の取り違えに注意します(int[]List<Integer> は別物)。


例題で身につける

例 1: フィルタ+集計(対象外をスキップ)

public class SumValid {
    public static void main(String[] args) {
        int sum = 0;
        for (int v : new int[] {3, -1, 8, -5, 2}) {
            if (v < 0) continue;
            sum += v;
        }
        System.out.println(sum); // 13
    }
}
Java

例 2: 文字列の整形出力(最後だけ区切りを出さない)

public class PrintWithComma {
    public static void main(String[] args) {
        String[] a = {"Java", "is", "fun"};
        for (int i = 0; i < a.length; i++) {
            boolean last = (i == a.length - 1);
            System.out.print(a[i]);
            if (!last) System.out.print(", ");
        }
    }
}
Java

「位置依存の処理」は通常の for を選ぶ例です。

例 3: マップのエントリ反復

import java.util.Map;

public class MapEach {
    public static void main(String[] args) {
        Map<String, Integer> m = Map.of("A",1, "B",2);
        for (Map.Entry<String, Integer> e : m.entrySet()) {
            System.out.println(e.getKey() + "=" + e.getValue());
        }
    }
}
Java

設計の指針(重要部分のまとめ)

  • 全件処理と読みやすさが最優先なら for-each。インデックスや逆順が必要なら通常の for を選ぶ。
  • 配列の値更新は for-eachではできない(変数再代入は反映されない)。更新は添字で。
  • コレクションの構造変更はイテレータで安全に。for-each中の remove/add は避ける。
  • 2次元は外側で行、内側で要素。マップはキー/値/エントリを明確に選ぶ。
  • break/continue は通常の for と同様に使える。null を含む要素はガードで守る。

タイトルとURLをコピーしました