広告

Java : for文の基本 (文法)

for文は、プログラムを繰り返し処理するための制御文です。
while文 と比べると、繰り返し回数を変数で制御したいときに向いています。

本記事では、Java の for文のうち、基本for文と呼ばれる文法について解説します。

もう1つの for文である 拡張for文については、下記の記事でまとめています。
そちらもぜひ参考にしてみてくださいね。

※本記事では、単に "for文" と書いているときは "基本for文" のことをさします。


基本for文

for文(フォーぶん)はプログラミング言語において条件が真の間だけ与えられた文の実行を繰り返すというループを記述するための文である。

基本for文は、プログラムを繰り返し処理させるための制御文です。

for (初期化処理; 条件式; 更新処理) {
    ... ループ文 ...
}

これが基本の形となります。

コード例で見てみましょう。

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

// 結果
// ↓
//i = 0
//i = 1
//i = 2
//i = 3
//i = 4

for文が開始されると、まず 初期化処理 が実行されます。

int i = 0

この部分ですね。
変数 i を宣言して 0 で初期化しています。

次に 条件式 がチェックされます。
そして、条件式が true の間は、ループ文と更新処理を繰り返し実行します。

条件式は

i < 5

の部分です。

i 変数は 0 で初期化したので、条件式は最初 0 < 5 となり、結果は true ですね。
よって、ループ文 が実行されます。

ループ文は

System.out.println("i = " + i);

の部分です。
※System.out.println は、値をコンソールに出力するためのメソッドです。

ループ文の実行が終わると、更新処理 が実行されます。
更新処理は、

i++

の部分です。
i 変数に 1 を加算します。

ここまで終わると、あとは 条件式 のチェックに戻って繰り返します。
(初期化処理 は最初の1回だけ実行されます)

i 変数は、繰り返しのたびに 0 → 1 → 2 → 3 ... と増えていきます。
そして、i が 5 になった次の条件式のチェックは 5 < 5 となり、結果は false で for文が終わります。

コンソールには、

i = 0
i = 1
i = 2
i = 3
i = 4

と出力されます。

もう一度、処理の流れをまとめてみましょう。

for (int i = 0; i < 5; i++) {
    System.out.println("i = " + i);
}
  1. for文開始
    1. 初期化処理を実行。

    2. 条件式をチェック。true であればループ文を実行。false であれば for文は終了。
    3. ループ文を実行。
    4. 更新処理を実行。
    5. 条件式のチェックに戻る。

  2. for文終了

フローチャート図で書くと次のようになります。

フローチャート図(for文)

それでは、もう少し詳しく見ていきましょう。

ノート

  • for文のカウンタ変数の名前には、よく i が使われます。なぜ i なのでしょうか?
    数学の変数名で使われているから、最初期のプログラミング言語である FORTRAN の整数型の変数宣言が I だから、などなど所説あるようです。
  • ともあれ、このような慣習には従っておいたほうがよいでしょう。
    なぜなら、他のプログラマが i を見たときに「あ、この変数は for文のためのカウンタなんだな」とすぐに理解できるからです。さらに、変数が1文字なのでコードもすっきりしますしね。

  • 参考:language agnostic - Why are variables "i" and "j" used for counters? - Stack Overflow

初期化処理

for (初期化処理; ; ) {
    ... ループ文 ...
}

初期化処理は、for文が開始された直後に1回だけ実行されます。
主な用途としては、カウンタ変数の宣言および初期化です。

for (int i = 0; ...; ...) {
    ...
}

上記の例では、変数 i を宣言して 0 で初期化しています。
変数宣言以外では、 や、クラスのメソッド呼び出しなども書くことができます。

int num = 0;

// コンパイルOK
for (num += 10; ; ) {
    ...
}

// コンパイルOK
for (System.out.println("abc"); ; ) {
    ...
}

しかし、 は書けません。コンパイルエラーになります。

int num = 5;

// コンパイルエラー
for (num + 5; ; ) {
    ...
}

// コンパイルエラー
for (num == 5; ; ) {
    ...
}

初期化処理で宣言された変数は、for文の内側のみで使えます。

for (int i = 0; i < 5; i++) {
    // コンパイルOK
    System.out.println("i = " + i);
}

for文の外側では使えません。スコープ外となります。

for (int i = 0; i < 5; i++) {
    ...
}

// コンパイルエラー
System.out.println("i = " + i);

もし初期化処理が不要なときは、 にすることもできます。

// 意図的に、変数 i の宣言を初期化処理の外側にします。
int i = 0;
for (; i < 5; i++) {
}

// コンパイルOK
System.out.println("i = " + i);

条件式

for (; 条件式; ) {
    ... ループ文 ...
}

条件式は、結果が boolean(真偽値) となる のみ許容します。
boolean にならないものはコンパイルエラーとなります。

例えば <> による大小の比較や、== による比較は、結果が boolean となる式です。

// 式 : 5 > 3
final var exp1 = 5 > 3;
System.out.println(exp1); // true

// 式 : 4 < -1
final var exp2 = 4 < -1;
System.out.println(exp2); // false

// 式 : 10 == 10
final var exp3 = 10 == 10;
System.out.println(exp3); // true
int num = 0;

// コンパイルOK
for (; num > 3; ) {
    ...
}

// コンパイルOK
for (; num < -1; ) {
    ...
}

// コンパイルOK
for (; num == 10; ) {
    ...
}

もしくは、結果が boolean となるメソッドの呼び出しも OK です。
例えば、文字列の比較に使う equals メソッドは boolean を返します。

final var exp1 = "abc".equals("abc");
System.out.println(exp1); // true

final var exp2 = "abc".equals("xyz");
System.out.println(exp2); // false
var str = "abc";

// コンパイルOK
for (; str.equals("xyz"); ) {
    ...
}

複数の条件式を使いたい場合は &&|| といった論理演算も使えます。

final var exp1 = 5 > 3;
System.out.println(exp1); // true

final var exp2 = "abc".equals("xyz");
System.out.println(exp2); // false

final var exp3 = exp1 && exp2;
System.out.println(exp3); // false

final var exp4 = exp1 || exp2;
System.out.println(exp4); // true
var num = 5;
var str = "abc";

// コンパイルOK
for (; num > 3 && str.equals("xyz"); ) {
    ...
}

// コンパイルOK
for (; num > 3 || str.equals("xyz"); ) {
    ...
}

足し算、引き算など +- を使った式は、結果が数値となります。
boolean ではないので for文の条件式には使えません。

// 式 : 5 + 3
final var exp1 = 5 + 3;
System.out.println(exp1); // 8

// 式 : 4 - 1
final var exp2 = 4 - 1;
System.out.println(exp2); // 3
var num = 5;

// コンパイルエラー
for (; num + 3; ) {
    ...
}

// コンパイルエラー
for (; num - 1; ) {
    ...
}

条件式は省略することができます。
その場合は、true を直接指定したときと同じになります。

// 条件式を省略。
for (; ; ) {
    ...
}

// こう書くのと同じです。
for (; true; ) {
    ...
}

補足

  • Java以外のプログラミング言語には、条件式に boolean 以外を許容しているものもあります。
    例えば C言語では、条件式に数値も許容しています。 0 は false、それ以外は true として扱います。
  • しかし、Java では厳密に boolean のみを許容します。0 や 1 といった数値を条件式に使うとコンパイルエラーになります。

更新処理

for (; ; 更新処理) {
    ... ループ文 ...
}

更新処理は、ループ文が終わるたびに実行されます。
主な用途としては、カウンタ変数の更新です。

for (...; ...; i++) {
    ...
}

上記の例では、ループ文が終わるたびに、変数 i を +1 します。

更新処理では、 やクラスのメソッド呼び出しなどを書くことができます。

int num = 0;

// コンパイルOK
for (; ; num += 10) {
    ...
}

// コンパイルOK
for (; ; System.out.println("abc")) {
    ...
}

は書けません。コンパイルエラーになります。

int num = 5;

// コンパイルエラー
for (; ; num + 5) {
    ...
}

// コンパイルエラー
for (; ; num == 5) {
    ...
}

初期化処理には書けた変数の宣言もできません。

// コンパイルエラー
for (; ; int i = 0) {
    ...
}

もし更新処理が不要なときは、 にすることもできます。

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

// 結果
// ↓
//i = 1
//i = 2
//i = 3
//i = 4
//i = 5

break文

for (; ; ) {
    ... ループ文 ...

    if (...) {
        break;
    }
}

さて、今までのコード例では、for文を終わらせるには条件式を false にする必要がありました。

しかし、条件式以外の任意の箇所で for文から抜けたい (終わらせたい) ことがあるかもしれません。
そんなときに使えるのが break文です。

break文は、ループ文の中で次のように書きます。

break;

コード例で見てみましょう。

for (int i = 0; ; i++) {
    System.out.println("------");
    System.out.println("loop : start");

    System.out.println("  i = " + i);

    if (i == 2) {
        System.out.println("Break!");
        break;
    }

    System.out.println("loop : end");
}

// 結果
// ↓
//------
//loop : start
//  i = 0
//loop : end
//------
//loop : start
//  i = 1
//loop : end
//------
//loop : start
//  i = 2
//Break!

今回は、for文を break文で終わらせたいので、条件式は省略しています。
これで、条件式では for文は終わらなくなりました。

for (int i = 0; ; i++) {
    ...
}

ループ文が繰り返されるたびに、更新処理で i 変数を 1 ずつ加算します。
そして、i == 2 になったときに "Break!" と出力して break文 で for文を抜けます。

System.out.println("------");
System.out.println("loop : start");

System.out.println("  i = " + i);

if (i == 2) {
    System.out.println("Break!");
    break;
}

System.out.println("loop : end");

結果は次のように出力されます。

------
loop : start
  i = 0
loop : end
------
loop : start
  i = 1
loop : end
------
loop : start
  i = 2
Break!

ここで注目したいのが、i = 2 のときのループ処理では、最後の "loop : end" が出力されない ことです。
break文で for文を抜けると、break以降の処理は実行されません。


continue文

for (; ; ) {
    ... ループ文 ...

    if (...) {
        continue;
    }
}

continue文を使うと、ループ文の処理をそこでやめて for文の更新処理へジャンプする、という制御ができます。

continue文は、ループ文の中で次のように書きます。

continue;

文章では少しわかりにくいかもしれません。
コード例で見てみましょう。

for (int i = 0; i < 4; i++) {
    System.out.println("------");
    System.out.println("loop : start");

    System.out.println("  i = " + i);

    if (i % 2 == 1) {
        System.out.println("Continue!");
        continue;
    }

    System.out.println("loop : end");
}

// 結果
// ↓
//------
//loop : start
//  i = 0
//loop : end
//------
//loop : start
//  i = 1
//Continue!
//------
//loop : start
//  i = 2
//loop : end
//------
//loop : start
//  i = 3
//Continue!

条件式は i < 4 です。

for (int i = 0; i < 4; i++) {
    ...
}

ループ文が繰り返されるたびに、更新処理で i 変数を 1 ずつ加算します。
そして、i が奇数のとき (i % 2 == 1) は "Continue!" と出力して continue文 を実行します。

System.out.println("------");
System.out.println("loop : start");

System.out.println("  i = " + i);

if (i % 2 == 1) {
    System.out.println("Continue!");
    continue;
}

System.out.println("loop : end");

結果は次のように出力されます。

------
loop : start
  i = 0
loop : end
------
loop : start
  i = 1
Continue!
------
loop : start
  i = 2
loop : end
------
loop : start
  i = 3
Continue!

i が偶数のときは continue文は実行されません。
そのため、ループ処理の最初で "loop : start" と出力され、ループ処理の最後で "loop : end" と出力されます。

i が奇数のときは continue文が実行されます。
continue文が実行されると、すぐに for文の更新処理 (i++) へジャンプします。"loop : end" は出力されません


無限ループに注意

無限ループ(むげんループ)は、コンピュータ・プログラム等の一連の手続き等が無限に繰り返される(ループする)ことである。永久ループ(えいきゅうループ)ともいう。

基本for文を使うときは、無限ループに注意しましょう。

もし無限ループが起きてしまうと、不具合の症状の1例としては、アプリは反応がなくなりフリーズしてしまいます。
アプリのクラッシュと並んで、致命的なバグの1つですね。

次のコード例を見てみましょう。

for (double num = 1.0; num > 0.0; num *= 10) {
    System.out.println(num);
}

num は浮動小数点数(double)です。

num が 0 より大きい間、ループを続けます。
そして、更新処理では num を 10倍していきます。

出力は次のようになります。

10.0
100.0
1000.0
10000.0
100000.0
1000000.0
... 省略 ...
9.999999999999999E306
9.999999999999998E307
Infinity
Infinity
Infinity
... 省略 ...

num の値が、10 → 100 → 1000 → 10000 ... と増えているのがわかりますね。
さらに続けていくと、最終的には正の無限大(Infinity) となります。

Infinity は、そこからいくら 10倍しても Infinity のままです。
そして Infinity は 0 より大きいので、この for文は永遠に実行し続けることになります。

これが無限ループです。

基本for文を使うときは、条件式や break文で、きちんとループが終了するかを常に気にかけなければなりません。
これがなかなか神経を使うのですよね…

もし 拡張for文 (for-eachループ文) が使えるケースであれば、そちらを使うことをおすすめします。
拡張for文は、なんと条件式を使わずにループ処理が可能です。(その代わり使えるケースは限定的です)

{ } の省略

for (初期化処理; 条件式; 更新処理)
    ループ文

ループ文が 1つの文 のみの場合は { } を省略することができます。

// 通常の書きかた
for (int i = 0; i < 3; i++) {
    System.out.println("i = " + i);
}

// こう書いたり…
for (int i = 0; i < 3; i++)
    System.out.println("i = " + i);

// こう書くこともできます。
for (int i = 0; i < 3; i++) System.out.println("i = " + i);

ただし、{ } を省略すると、思わぬコーディングミスを誘発してしまうかもしれません。

次のコード例を見てみましょう。

var num = 0;
for (; num < 3; ) System.out.println("num = " + num); num++;

さて、ぱっと見て、どのような結果になるかわかりますでしょうか。

実は、この for文は無限ループします。
コンソールには

num = 0
num = 0
num = 0
num = 0
num = 0
...

と、"num = 0" が出力され続けます。

なぜこのような結果になるのか考えてみましょう。

for文の { } を省略したので、ループ文は

System.out.println(num);

1つの文 だけが対象となります。
その後ろの

num++;

の文は、for文の外側となります。
つまり次のコードと同じ意味になります。

var num = 0;
for (; num < 3; ) {
    System.out.println("num = " + num);
}
num++;

num 変数は for文で変わることはないので、いつまでも条件式は false にならず無限ループしてしまう…というわけでした。

{ } がないと、どこまでが ループ文なのか分かりにくくなることがあります。
個人的には、1つの文のみの場合でも { } は省略しないほうがよいんじゃないかな…と思います。


代替えとなる方法

Java言語には、for文の代わりとなるものがいくつか用意されています。
ここではそれらを簡単にご紹介します。

while文

初期化処理と更新処理が不要なときは while文が使えます。

while (条件式) {
    ... ループ文 ...
}

これが基本の形となります。

処理の流れは次のようになります。

  1. while文開始
    1. 条件式を判定。true であればループ文を実行。false であれば while文は終了。
    2. ループ文を実行。
    3. 条件式の判定に戻る。
  2. while文終了

コード例です。

{
    int i = 0;
    while (i < 3) {
        System.out.println("i = " + i);
        i++;
    }
}

// 結果
// ↓
//i = 0
//i = 1
//i = 2

これは for文を使って次のように書くのと同じです。

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

// 結果
// ↓
//i = 0
//i = 1
//i = 2

while文については、よろしければ下記の記事もご参照ください。


拡張for文

for (T value : "配列""Iterable" を実装したクラス) {
    ...
}

拡張for文は、配列 もしくは Iterable を実装したオブジェクトに対して、繰り返し処理を行います。
例えば List は Iterable を実装しています。

条件式は必要とせず、すべての要素に対して繰り返し処理が実行できます。

Iterable は基本有限なので、無限ループの心配はほぼありません。 (実装次第では無限にもできますが…)

List の要素に順次アクセスして出力する例です。

final var list = List.of("aaa", "bbb", "ccc");

for (final var value : list) {
    System.out.println("value = " + value);
}

// 結果
// ↓
//value = aaa
//value = bbb
//value = ccc

これは 基本for文を使って次のように記述するのと同じです。

final var list = List.of("aaa", "bbb", "ccc");

for (final var it = list.iterator(); it.hasNext(); ) {
    final var value = it.next();
    System.out.println("value = " + value);
}

// 結果
// ↓
//value = aaa
//value = bbb
//value = ccc

拡張for文については、よろしければ下記の記事もご参照ください。

Stream API

拡張for文をさらに発展させたものとして、Stream API があります。
こちらは少し難易度が高めなので、Javaに慣れてからでよいとは思います。

Stream API を使って List の要素を大文字に変換してから出力する例です。

final var list = List.of("aaa", "bbb", "ccc");

list.stream().map(String::toUpperCase).forEach(value -> {
    System.out.println("value = " + value);
});

// 結果
// ↓
//value = AAA
//value = BBB
//value = CCC

Stream API については下記の記事で詳しく解説していますので、興味がありましたらご参照ください。

公式ドキュメント

14.14.1. The basic for Statement
The basic for statement executes some initialization code, then executes an Expression, a Statement, and some update code repeatedly until the value of the Expression is false.

さらに詳しく 基本for文について知りたい場合は、公式のJava言語仕様に記載があります。
(残念ながら公式の日本語訳はないようです)

まとめ

基本for文を使うと、プログラムを繰り返し処理させることができます。
特に、繰り返し回数を変数で管理したいときに便利です。

ただし、場合によっては 基本for文より優れた方法もあります。
例えば、拡張for文Stream API です。

もしそれらが使える状況であれば、ぜひそちらを使うことも検討してみましょう。


関連記事

ページの先頭へ