PR

Javaのfor文:繰り返し処理の基本から応用までを3分で解説

Java

Javaのfor文は、特定の処理を何回も繰り返すための構文(制御構文)の1つです。繰り返し処理(ループ処理)というのは、プログラミングをする上で非常に頻出する重要なテクニックで、たとえば配列の要素を順番に表示したり、特定の条件に合致するものを探したりするときなど、頻繁にループを使います。

Javaには繰り返し処理として、主に以下の3種類の構文があります。

  1. for
  2. while
  3. do-while

Java 5以降では要素を順番に取り出すのに便利な“拡張for文”(または“拡張forループ”とも呼ばれる)も導入されました。これも広義にはfor文の一種ですので、このページで合わせて解説します。ここではまず、最も一般的な通常のfor文の書き方から順を追ってご説明していきます。

スポンサーリンク

for文の基本構文

それでは、Javaの標準的なfor文がどのように書かれるかを見てみましょう。基本的には以下の構文です。

for (初期化; 条件式; 変化式) {
    // 繰り返し実行したい処理
}
  • 初期化: ループが開始される前に一度だけ実行される部分です。ここでは変数を宣言したり、その変数に初期値を設定したりします。
  • 条件式: ループを続けるかどうかを判断するための条件です。trueの間はループ本体の処理が実行され、falseになるとループを終了します。
  • 変化式: ループ本体を一度実行したあと、再度条件をチェックする前に行われる処理です。典型的にはカウンタ変数を1ずつ増やす(または減らす)といった操作をします。

ここでの特徴は、for文の丸括弧の中に「初期化」「条件式」「変化式」をまとめて書ける点です。例として、変数iが0から10まで1ずつ増えていく場合の基本的な書き方は次の通りです。

for (int i = 0; i <= 10; i++) {
    System.out.println(i);
}

このループが具体的にどのように動くかをステップで説明すると、以下のようになります。

  1. int i = 0; → 変数iを宣言して0で初期化(ループ開始前に一度だけ実行)
  2. i <= 10 → 条件式をチェック。0 <= 10 は true なので、ループ本体を実行
  3. ループ本体の { System.out.println(i); } を実行 → 0 が表示される
  4. 変化式 i++iが 0 から 1 に変わる(参考 インクリメント演算子++
  5. 再び i <= 10 のチェック。1 <= 10 は true なので本体を実行
  6. System.out.println(i);1 が表示される
  7. i++i が1から2に変わる
  8. この繰り返しが、i <= 10false になるまで(つまり i が11になるまで)ずっと続く

結果として、0, 1, 2, 3, … 10 の数字が順番に表示されます。

for文を分解して理解する

もう少し噛み砕いて構文の部分を整理してみましょう。上記のコード

for (int i = 0; i <= 10; i++) {
    System.out.println(i);
}

を、あえてwhile文的に書き直すと、以下のような流れになります。

int i = 0;                // 初期化
while (i <= 10) {         // 条件式
    System.out.println(i); // ループ本体
    i++;                   // 変化式
}

このように見ると、「初期化」「条件式」「変化式」がそれぞれの場所で処理されているだけで、やっていることは同じです。ただしfor文のメリットとしては、1行(正確には1つの丸括弧内)にまとまっているため、どこでループのカウンタを増やしているのかが一目瞭然になり、コードが読みやすくなることがあります。

ループカウンタの増やし方を変える

典型的には、カウンタ変数を1ずつ増やす書き方(i++)が頻出しますが、用途によっては変化式を変えることも可能です。たとえば2ずつ増やしたい場合は i += 2 を使います。

for (int i = 0; i <= 10; i += 2) {
    System.out.println(i);
}

これを実行すると、0, 2, 4, 6, 8, 10 と表示されます。同様に、逆順(10から0へ向かって減らしていく)場合は次のようになります。

for (int i = 10; i >= 0; i--) {
    System.out.println(i);
}

ここでは、条件式も >= 0 に変える必要がある点に注意してください。もし i >= 0 にしなかったら、期待したタイミングでループを終了しなくなります。

実践編:for文の使いどころ

サンプルコード 合計値を求める

よくあるパターンの1つとして、何かの範囲内の数値を合計する例があります。たとえば 1 から 10 までの合計値を求めたい場合、次のように書けます。

int sum = 0;
for (int i = 1; i <= 10; i++) {
    sum += i;
}
System.out.println("1から10までの合計は: " + sum);

// 1から10までの合計は: 55 と表示される

このコードの流れ

  1. sumを0で初期化
  2. i = 1 からスタート、i <= 10 の間ループする
  3. ループ本体で sum += i; として sumi を加算
  4. i++i をインクリメント
  5. i が11になった時点でループ終了
  6. 最後に sum の値を表示

サンプルコード 素数を判定する(簡易版)

やや実用的な例として、ある数が素数かどうかを判定する方法もあります。たとえば、与えられた数 n のうち、2 から n-1 までのどこかで割り切れたら素数ではない、という仕組みで書くと簡易的にはこんな感じです。

int n = 29;
boolean isPrime = true;

for (int i = 2; i < n; i++) {
    if (n % i == 0) {
        // もし割り切れたら素数ではない
        isPrime = false;
        break; // これ以上調べる必要がないのでループを抜ける
    }
}

if (isPrime) {
    System.out.println(n + "は素数です");
} else {
    System.out.println(n + "は素数ではありません");
}

このように、ある一定の範囲を順番に調べるという場合には、for文が非常に便利です。なお、実際には素数判定する際に、i の上限を sqrt(n)(nの平方根)にすれば計算量を減らせるなどの最適化がありますが、ここでは「ループで範囲を回してチェックする」例として覚えておくと良いでしょう。

break文とcontinue文

for文を使っていると、途中でループを抜けたい場合や、一部の繰り返しだけ飛ばしたい場合があります。そんなときに役立つのがbreak文とcontinue文です。

break文

break文は、現在実行中のループを強制的に終了させたいときに使います。先ほどの素数判定の例でも使われていましたが、以下のように「ある条件を満たしたらすぐにループから抜ける」といった処理が必要な場合に便利です。

for (int i = 0; i <= 10; i++) {
    if (i == 5) {
        break; // i が5になったらループを抜ける
    }
    System.out.println(i);
}

// 0, 1, 2, 3, 4 だけが表示され、i が 5 になった時点でループを終了

continue文

一方、continue文は、その回のループ処理だけスキップして次の反復に移りたいときに使われます。たとえば以下のコードでは、i が 5 のときだけは System.out.println(i); を実行せずに次の i++ に進んでいます。

for (int i = 0; i <= 10; i++) {
    if (i == 5) {
        continue; // i が 5 のときは表示せずに次のループへ
    }
    System.out.println(i);
}

// 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 が表示され、5 だけが飛ばされる

ネストされたfor文(多重ループ)

プログラミングでは、ときどきループの中にさらに別のループが入れ子になっている(ネストされている)構造が必要になります。たとえば2次元配列を使っている場合の処理や、表形式のデータを扱う場合などに典型的です。

多重ループは、文法的には単にループの中に別のループを置くだけです。

例として、掛け算九九表を表示するプログラムを書いてみましょう。

for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= 9; j++) {
        System.out.print(i * j + " ");
    }
    System.out.println(); // 改行
}

ここでは、外側のループで i が1から9まで回り、内側のループで j が1から9まで回ります。結果として、掛け算の組み合わせが 11, 12, …, 19, 21, 22, …, 99 のように順番に計算され、それを1行ごとにまとめて表示していきます。

多重ループはネストの深さが増えると、何が起こっているのかを理解するのが難しくなりがちです。特に三重ループ、四重ループとなると一気に複雑になります。最初は紙にフローチャートを書いてみたり、小さい数値で実際に手計算しながら追いかけてみたりすると良いでしょう。

拡張for文(Enhanced for文)

Java 5以降では、配列コレクション(ListやSetなど)に含まれる要素を1つずつ順番に取り出すための“拡張for文”が導入されました。正式な名前は “Enhanced for statement” ですが、一般的には “拡張for文” と呼ばれます。

拡張for文の構文は以下のようになります。

for (型 変数名 : 配列やコレクション) {
    // 要素を使った処理
}

例えば、int型の要素を持つ配列 numbers があったとき、それを順番に表示するには次のように書きます。

int[] numbers = { 10, 20, 30, 40, 50 };

for (int num : numbers) {
    System.out.println(num);
}

これだけで、配列 numbers の各要素が1つずつ順番に num に代入され、ループ本体が実行されます。通常のfor文で同じことをすると、以下のようになるでしょう。

for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

つまり、配列の長さなどを意識しなくても、拡張for文を使うことで各要素を取り出す処理が簡潔に書けるようになります。コレクション(ListやSetなど)でも同様に扱うことができるので、非常に便利です。

ただし、拡張for文は「要素を参照して使うだけ」の場合には便利ですが、配列の要素をインデックスでアクセスして変更したいときや、ループ中にイテレータの状態を直接操作したいときには不向きです。その点は注意してください。

for文を使う上での注意点・ベストプラクティス

条件式を正しく書く

i <= 10 なのか、i < 10 なのか」など、境界条件を間違えると意図しない回数だけループが回ったり、逆に1回分足りなかったりすることがあります。配列の要素にアクセスするループでは、インデックス範囲を超えないよう注意が必要です。

i < numbers.length と書くべきところを i <= numbers.length と書くと ArrayIndexOutOfBoundsException が発生します。

無限ループに注意

条件式が常に true になってしまうと、ループが終わりません。例えば以下は無限ループになる典型例です。

for (int i = 0; i <= 10; ) {
    System.out.println(i);
    // i++ がないため、iはずっと0のまま
}

変化式をうっかり書き忘れたり、条件式のほうを間違えてしまうと、プログラムが延々と動き続けてしまうので気をつけましょう。

カウンタ変数のスコープ

for (int i = 0; i < 5; i++) {
    // i はここのブロック内でのみ有効
}
// ここでは i は参照できない

for文で宣言したカウンタ変数(int i = 0; のように書く部分)は、そのfor文のスコープ内だけ有効です。ループの外でこの変数にアクセスしようとするとコンパイルエラーになります。もしループの外でも同じ変数を使いたい場合は、ループの外で宣言だけしておく必要があります。

変数名や使い方の意味を分かりやすく

典型的にカウンタ変数として i, j, k などを使うことが多いですが、処理内容によってはより意味のある名前をつけたほうが読みやすい場合もあります。読みやすいコードを心がけるのも重要です。

条件文では複数変数を利用できない

Javaのfor文は、以下のような基本構文を持っています。

for (初期化文; 条件文; 更新文) {
    // ループ本体
}
  • 初期化文(Initialization): ループ開始前に変数を宣言・初期化できる部分
  • 条件文(Condition): ループを続けるかどうか判定するための単一のboolean式
  • 更新文(Update): 各反復の最後に実行される式(ふつうカウンタ変数のインクリメントなど)

このうち、初期化文と更新文はカンマ区切りで複数の処理をまとめられます。

for (int i = 0, j = 10; i < j; i++, j--) {
    // ...
}

では、初期化文で ij の両方を同時に宣言・初期化し、更新文で i++j-- の両方を行っています。一方で、条件文は単一のboolean式でなければならないという決まりがあります。複数の条件を組み合わせたい場合は、論理演算子(&&||)を使って1つの式にすることはできますが、変数の宣言や複数の式をカンマで並べることはできません。

したがって、「for文で複数の変数を“宣言”・“初期化”や“更新”をまとめて行えるのは、初期化文と更新文だけ」というのがポイントです。条件文には本来「boolean値を返す式」が1つ必要なだけであり、そこでは新たな変数を宣言したり、カンマ区切りでいくつもの式を並べたりする構文は許されていない、というのがJavaの仕様になっています。

初期化部分で複数の変数を宣言するときは同じデータ型にする

Javaのfor文の初期化部分で複数の変数を宣言するときは、同じ型の変数でなければなりません。たとえば次のような書き方はOKです。

for (int i = 0, j = 10; i < j; i++, j--) {
    // ...
}

ij の両方が int なので同じ型としてまとめて初期化できます。

一方、以下のように型が異なる変数を同時に宣言することはできません。

// コンパイルエラーになる例
for (int i = 0, double d = 3.14; i < 10; i++) {
    // ...
}

変更式の実行タイミング

for文では「初期化部 → 条件式の評価 → ループ本体 → 変更式」の順で実行され、特に変更式は毎回ループ本体が実行された後に呼び出されるため、例えばメソッド内で副作用(コンソール出力など)を行う場合は、各反復の終わりにその処理が実行されることになります。

public class ForLoopExample {
    public static void main(String[] args) {
        // 初期化部:iを0で初期化
        for (int i = 0; i < 3; i = increment(i)) {
            // ループ本体
            System.out.println("ループ本体: i = " + i);
        }
    }

    // 変更式として利用するメソッド
    public static int increment(int i) {
        System.out.println("変更式が実行されました。 現在の i: " + i);
        return i + 1;
    }
}
  1. 初期化部
    ループ開始前に、int i = 0; により変数iが初期化されます。
  2. 条件式の評価
    ループの各回の先頭で i < 3 の条件が評価され、条件が真ならループ本体が実行されます。
  3. ループ本体の実行
    条件が満たされると、ループ本体が実行されます。この例では System.out.println("ループ本体: i = " + i); が実行されます。
  4. 変更式(メソッド呼び出し)の実行
    ループ本体の実行後、更新部に記述された i = increment(i) が実行されます。このとき、increment メソッドが呼ばれ、iの値に対して処理が行われた後、戻り値が新たな i の値となります。
    つまり、ループの1回の反復(イテレーション)の終わりに必ずこの更新処理が実行され、次の反復に向けた値の更新が行われます。
  5. 再び条件式の評価
    更新後、再度条件式が評価され、条件が真であれば次のループ本体が実行されます。条件が偽になればループを抜けます。

for文活用の発展例

多次元配列の処理

2次元配列や3次元配列などを扱う場合は、ネストされたfor文がほぼ必須になります。典型例として、2次元配列に格納されたデータをすべて表示するには、外側のforで行(row)を、内側のforで列(column)を回します。例を示します。

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

for (int row = 0; row < matrix.length; row++) {
    for (int col = 0; col < matrix[row].length; col++) {
        System.out.print(matrix[row][col] + " ");
    }
    System.out.println();
}

この場合、matrix.length は行の数、matrix[row].length はそれぞれの行に含まれる列の数です。Javaでは2次元目以降の配列のサイズが行ごとに異なる「ジャグ配列」を扱えるので、列のサイズを正確に把握するために matrix[row].length のように書くのが定番パターンです。

コレクションとの組み合わせ

拡張for文との合わせ技で、たとえばList<String> の中にあるデータを順番に処理したい場合は以下のように書けます。

List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");

for (String name : names) {
    System.out.println(name);
}

内部的には、Listのイテレータを使って要素が1つずつ取り出されています。Java 8以降ではラムダ式やストリームAPIも登場し、より宣言的にコレクションを処理できるようになりましたが、拡張for文もシンプルでわかりやすい書き方としてよく利用されます。

まとめ for文の基本

  1. 基本構文: for (初期化; 条件式; 変化式) { … }
    • 初期化、条件式、変化式が1つのまとまりになっており、読みやすく保守しやすい。
  2. カウンタの増やし方: i++だけでなく、i += 2i--など自在に書ける。
  3. break文で途中終了、continue文でその反復をスキップといった制御も可能。
  4. ネストしたfor文で多次元の処理を扱える。使いどころを間違えないようにし、読みやすさにも気を配る。
  5. 拡張for文で配列やコレクションの要素をシンプルに取り出せる。
  6. 無限ループ配列外アクセスなど、基本的なバグには注意する。
public class ForLoopExamples {
    public static void main(String[] args) {
        // 1. カウンタ変数を使ったループ
        for (int i = 0; i < 5; i++) {
            System.out.println("繰り返し回数: " + i);
        }
        // 出力結果:
        // 繰り返し回数: 0
        // 繰り返し回数: 1
        // 繰り返し回数: 2
        // 繰り返し回数: 3
        // 繰り返し回数: 4

        // 2. 配列の要素を処理する
        int[] numbers = {1, 2, 3, 4, 5};
        for (int i = 0; i < numbers.length; i++) {
            System.out.println("配列の要素: " + numbers[i]);
        }
        // 出力結果:
        // 配列の要素: 1
        // 配列の要素: 2
        // 配列の要素: 3
        // 配列の要素: 4
        // 配列の要素: 5

        // 3. 拡張for文(for-eachループ)
        for (int number : numbers) {
            System.out.println("配列の要素: " + number);
        }
        // 出力結果:
        // 配列の要素: 1
        // 配列の要素: 2
        // 配列の要素: 3
        // 配列の要素: 4
        // 配列の要素: 5
    }
}
タイトルとURLをコピーしました