Javaについて徹底解説!

ループを次に”続ける”! Javaのcontinueでfor/while文を制御しよう

大石 英人

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

Javaのcontinueは、for文やwhile文などのループ文で「今のループを終わらせて、次のループを始める」ために使うものです。continuebreakと並んで、Javaでのループをコントロールするための重要な機能の一つです。

continueは機能としては大変シンプルなものです。でも、例えばfor文との組わせでは、for文が動く仕組みをきちんと理解していないと「え、どういうこと?」となりがちです。さらにwhile文でもちょっとしたワナが待っています。

プログラムでは、for文やwhile文がいくつも重なりあってしまって、とても「深く」なることがあります。その場合のcontinueはなかなか大変なのですが、そのような時に使える機能もcontinueにはちゃんと備わっています。

この記事では、continueの基本から少し発展した話題まで、初心者向けに幅広く扱います。

※この記事のサンプルは、Java 11の環境で動作確認しています


1.continueとはどういうものか

1-1.continueとは次のループをすぐ始めるもの

continueは今のループをすぐ終わらせて、次のループを始めるための構文です。continueはループ文、つまりfor/while/do-while文の中にしか書けません。ループを制御することが目的の構文だからです。

continueは、言い換えれば「もうこのループではもうやることがないから、次のループに行くよ」ということです。そのため、やることがないと判断するif文やswitch文との組み合わせが普通です。もちろん、ループの最後でcontinueしてもいいのですが、あまり意味があることではありません。

なお、continueを実行することを「continueする」ともプログラマはよく言います。この記事でも以後はその表現を使いますので、慣れておくといいでしょう。

1-2.continueの書き方

continueの書き方はとてもシンプルで、ループ文中のどこかで以下のように書くだけです。

continue;

continueにはこのように何もパラメータを指定しないのが普通です。でも、実はcontinueにはラベルと呼ばれるものをパラメータにできます。

ただ、ラベル付きのcontinueは、そうそう使うものではありません。私自身も実務のJavaプログラムでは使ったほとんど覚えがありません。でも、知っておくのは無駄にはなりません。そんなパラメータ付きのcontinueは、別の章でお伝えします。

1-3.for文/拡張for文でのcontinueの動き

for文は、以下のように分解できます。continueは、for文の中ではの処理部でならどこにでも書けて、またcontinueすると継続式が実行されます。

for (①初期化式; ②条件式; ④継続式) {
	③処理部
}

では、実際にfor文でのcontinueを試してみましょう。i1のループだけが途中で終わって、次のループが行われていることが分かるでしょうか。

System.out.println("for文の前です");

for (int i = 0; i < 3; i++) {
	System.out.println("今は" + i + "です");

	if (i == 1) {
		System.out.println("continueします");
		continue;
	}

	System.out.println(i + "の処理の終わりです");
}

System.out.println("for文の後です");

実行結果

for文の前です
今は0です
0の処理の終わりです
今は1です
continueします
今は2です
2の処理の終わりです
for文の後です

continueの動きをもう少しわかりやすくするために、for文の各部分でprintしてみます。continueをすると、その次に動くのは条件式ではなく、継続式ですよね。これでcontinueするとどこに処理が移るか、具体的にお分かりいただけたでしょうか。

class ContinueSample {
	static int init() {
		System.out.println("①初期化式です");
		return 0;
	}

	static boolean condition(int i) {
		System.out.println("②条件式です: " + i + " < 3");
		return i < 3;
	}

	static void next(int i) {
		System.out.println("④継続式です: i = " + i);
	}

	public static void main(String[] args) {
		System.out.println("for文の前です");

		for (int i = init(); condition(i); next(i), i++) {
			System.out.println("③処理部です、今は" + i + "です");

			if (i == 1) {
				System.out.println("continueします");
				continue;
			}

			System.out.println(i + "の処理の終わりです");
		}

		System.out.println("for文の後です");
	}
}

実行結果

for文の前です
①初期化式です
②条件式です: 0 < 3
③処理部です、今は0です
0の処理の終わりです
④継続式です: i = 0
②条件式です: 1 < 3
③処理部です、今は1です
continueします
④継続式です: i = 1
②条件式です: 2 < 3
③処理部です、今は2です
2の処理の終わりです
④継続式です: i = 2
②条件式です: 3 < 3
for文の後です

拡張for文なら、あまり難しいことは考えなくてもOKです。ここでも、continue処理部にならどこにでも書けます。

for (①変数宣言部; ②対象指定部) {
	③処理部
}

こちらもプログラム例を示します。

int[] array = { 0, 1, 2 };
System.out.println("拡張for文の前です");

for (int i : array) {
	System.out.println("③処理部です、今は" + i + "です");

	if (i == 1) {
		System.out.println("continueします");
		continue;
	}

	System.out.println(i + "の処理の終わりです");
}

System.out.println("拡張for文の後です");

実行結果

拡張for文の前です
③処理部です、今は0です
0の処理の終わりです
③処理部です、今は1です
continueします
③処理部です、今は2です
2の処理の終わりです
拡張for文の後です

1-4.while文/do-while文でのcontinueの動き

while文/do-while文でも、continueの使い方はfor文と同じです。ループをしているところに書けて、continueすると条件式に処理が移ります。

int i = 0;
System.out.println("while文の前です");

while (i < 3) {
	System.out.println("今は" + i + "です");

	if (i == 1) {
		System.out.println("continueします");
		i++;
		continue;
	}

	System.out.println(i + "の処理の終わりです");
	i++;
}

System.out.println("while文の後です");

実行結果

while文の前です
今は0です
0の処理の終わりです
今は1です
continueします
今は2です
2の処理の終わりです
while文の後です

 

int i = 0;
System.out.println("do-while文の前です");

do {
	System.out.println("今は" + i + "です");

	if (i == 1) {
		System.out.println("continueします");
		i++;
		continue;
	}

	System.out.println(i + "の処理の終わりです");
	i++;
} while (i < 3);

System.out.println("do-while文の後です");

実行結果

do-while文の前です
今は0です
0の処理の終わりです
今は1です
continueします
今は2です
2の処理の終わりです
do-while文の後です

1-4-1.【参考】while文ではカウンター変数の加減算のし忘れに気を付けよう!!

前述した例でcontinueの前にカウンター変数iを加算しているのは、while/do-while文のcontinueでは地味に重要です。カウンター変数を加減算し忘れるのは、continueを使う上では大変よく起きるバグです。恥ずかしながら、私もやったことがあります。

カウンター変数とcontinueを使うなら、for文との組み合わせの方がより確実かもしれません。while文ではfor文と違って、カウンター変数の加算処理などのループ終了後の共通処理を確実にできるところがありませんので、continueの前にカウンター変数を自分で+1しておく必要があるのです。

単純なカウンターなら、以下のように条件式で行うことも普通です。前++(前置)と後ろ++(後値)のどちらにすべきかは、状況によります。ただし、この場合だと、ループ中でiを直接参照する時は気を付けましょう。前置・後値のいずれを使うにせよ、条件式が終わった時点で、既に+1されているからです。

while (i++ > 2) {

1-5.ネストしたループ文でのcontinueにはご注意を!!

for/while/do-while文はネスト(nest、入れ子、何かの中に何かを含むこと)させられます。ループ分がネストした状態でcontinueすると、continueしたところから「最も近い」ループが次のループになります。

例えば以下では、continueしているところから「上へ」たどっていって、もっとも近いループ文である「jfor文」をcontinueします。ですから「ifor文」はそのまま何事もなかったかのように続くのです。

System.out.println("ネストしたfor文を実行します");

for (int i = 0; i < 2; i++) {
	System.out.println("今は i = " + i + "です");

	for (int j = 0; j < 3; j++) {
		System.out.println("  今は j = " + j + "です");

		if (j == 1) {
			System.out.println("  continueします。jのfor文は次のループへ行きます");
			continue;
		}

		System.out.println("  j = " + j + "のループが終わりました");
	}

	System.out.println("i = " + i + "のループが終わりました");
}

System.out.println("ネストしたfor文を抜けました");

実行結果

ネストしたfor文を実行します
今は i = 0です
  今は j = 0です
  j = 0のループが終わりました
  今は j = 1です
  continueします。jのfor文は次のループへ行きます
  今は j = 2です
  j = 2のループが終わりました
i = 0のループが終わりました
今は i = 1です
  今は j = 0です
  j = 0のループが終わりました
  今は j = 1です
  continueします。jのfor文は次のループへ行きます
  今は j = 2です
  j = 2のループが終わりました
i = 1のループが終わりました
ネストしたfor文を抜けました

ですので、ループがネストした状態だと、意図したcontinueが難しくなりることがあります。親ループでcontinueできることが、ネストが深いところにあるループ文で分かった場合は面倒です。その場合は、ループ全体の継続要否を管理する変数を使ったりすることになります。

そういうプログラムは複雑になりがちです。ですが、そういう用途に使えるよう、いくらループが深くなっても親のループを子ループから直接continueするやり方があるのでご安心ください。それが前述したラベル付きcontinueです。

1-6.breakとの違いはループそのものを抜けるか

continueは今のループを中断して次のループを始めます。breakは今のループを中断してループ文そのものを終了します。ですので、breakは「もうループ文そのものをもう終わらせてもいいよ」と判断した時に使うので、ある意味ではcontinueよりも「強い」ものです。

System.out.println("for文の前です");

for (int i = 0; i < 3; i++) {
	System.out.println("今は" + i + "です");

	if (i == 1) {
		System.out.println("breakします");
		break;
	}

	System.out.println(i + "の処理の終わりです");
}

System.out.println("for文の後です");

実行結果

for文の前です
今は0です
0の処理の終わりです
今は1です
breakします
for文の後です

breakの詳細については、以下の記事も参照してください。

関連記事

2.【発展】for/while文のラベル付きcontinue

2-1.ラベルとループ文とcontinueの関係

ラベルとは、for/while/do-while文の前に付けられる、ループ文の名前のようなものです。そしてcontinueは、実はそのラベル付けしたループ文へ実行できるのです。

前章では「continueしたところから最も近いループ文が対象になる」と書きましたが、これはcontinueする対象をプログラマが指定しない場合の、デフォルトの動きです。

この章では、ラベル付きcontinueについて、ラベルのつけ方からスタートしつつ、簡単に触れてみます。

2-2.ラベルは文字列 + “:”でつける

ラベルは以下のように「ラベル文字列 + “:”」という形式で書きます。ラベル文字列に使える文字は変数名と同じですが、ラベルであることが分かりやすくするように大文字とすることが普通です。

ラベル文字列:

2-3.ループ文にラベルを付ける

for文/while/do-while文へラベル付けするには、以下のようにそれぞれのキーワードの「前」にラベルを置きます。

LABEL: for(int i = 0; i < 10; i++) {
	// ループ文の中身
}

LABEL: while(true) {
	// ループ文の中身
}

LABEL: do {
	// ループ文の中身
} while(true);

もちろん、ラベル付けしたループ文がたくさんネストしていても大丈夫です。それぞれのループ文に、ラベル付けがされます。

LABEL1: for (int i = 0; i < 2; i++) {
	// ここはLABEL1の中
	LABEL2: for (int j = 0; j < 2; j++) {
		// ここはLABEL2の中
		LABEL3: for (int k = 0; k < 2; k++) {
			// ここはLABEL3の中
		}
	}
}

ただし、同じラベルのループ文の中では、既に使われているラベルを使えません。ループ文の区別が付けられないからで、この考え方は変数のスコープと似ています。

ですが、外にある違うループ文同士なら同じラベルでも問題ありません。なぜかと言えば、ラベルの有効範囲は紐づけたループ文の中だけだからです。

LABEL: for (int i = 0; i < 2; i++) {
	LABEL: for (int j = 0; j < 2; j++) { // コンパイルエラー!! ラベル付けされたループ文の中では、同じラベルは使えない
	}
}
LABEL: for (int k = 0; k < 2; k++) { // 外にある違うループ文へなら同じラベルが使える
}

2-4.ラベルを使ったcontinue

いよいよラベル付きループ文をcontinueしてみます。以下のように、continueの後ろにcontinueしたいラベル文字列を指定しましょう。

continue ラベル文字列;
System.out.println("ラベル付きfor文に入ります");

LABEL1: for (int i = 0; i < 2; i++) {
	System.out.println("今は i = " + i + "です");

	LABEL2: for (int j = 0; j < 3; j++) {
		System.out.println("  今は j = " + j + "です");

		if (j == 1) {
			System.out.println("  LABEL1をcontinueします");
			continue LABEL1;
		}

		System.out.println("  j = " + j + "の終わりです");
	}

	System.out.println("i = " + i + "の終わりです");
}

System.out.println("ラベル付きfor文から抜けました");

実行結果

ラベル付きfor文に入ります
今は i = 0です
  今は j = 0です
  j = 0の終わりです
  今は j = 1です
  LABEL1をcontinueします
今は i = 1です
  今は j = 0です
  j = 0の終わりです
  今は j = 1です
  LABEL1をcontinueします
ラベル付きfor文から抜けました

実行結果を見ると、jのループの中から、iのループを直接continueできていることが分かると思います。ですから、ループ文のネストが深くなっても、ラベルさえつけておけば、直接「上」にあるループ文をcontinueできるのですね。


3.contiueといろいろ

3-1.continueとtry-finallyは少し注意しよう

continueとtry-finallyの組み合わせは、少しだけ注意しましょう。tryの中でcontinueすると、セットのfinallyも実行されます。finallyを差し置いて、すぐに次のループに行くわけではないのですね。ですから「これは実行されるはずじゃなかったのに」ということがないようにしましょう。

for (int i = 0; i < 3; i++) {
	System.out.println("今は" + i + "です");

	if (i == 1) {
		try {
			System.out.println("continueします");
			continue;
		} finally {
			System.out.println("continueでのfinallyです");
		}
	}

	System.out.println(i + "の処理の終わりです");
}

実行結果

今は0です
0の処理の終わりです
今は1です
continueします
continueでのfinallyです
今は2です
2の処理の終わりです

3-2.Streamでcontinueしたい時はreturnが近い意味

Java 8から追加されたStream APIはループ文に似た処理ができます。ですが、あくまで「ループに似たもの」であり、構文上のループ文ではないのでcontinueはできません。

List<String> list = Arrays.asList("A", "B", "C");
list.stream().forEach(s -> {
	continue; // ←コンパイルエラー!! for/whileのようなループ文の中ではないため、continueは行えない
});

Streamは、正確に言えば何かの集まりにある要素一つ一つに対して処理を並行に行うための仕組みであって、ループ的に何かをするわけではないからです。ですので、要素一つに対する処理をreturnで中断することはできますが、Stream全体を中断することはループ文と同じようにはできないのです。

ですので、ループ中でcontinue的な動きをする必要があるなら、従来どおりfor/while文を使うことになります。また、近い動きにしたいなら、Stream上で今のデータの処理をreturnで終わらせてしまえば、continueに近い動きになります。

List<String> list = Arrays.asList("A", "B", "C");
list.stream().forEach(s -> {
	// 引数のStringが"B"なら、処理をせずにreturnする。
	// 次のループを行うcontinueと、比較的近い動きにはなる。
	if ("B".equals(s)) {
		return;
	}
	System.out.println(s);
});

3-3.ループではなるべく早いタイミングでcontinueすべき

ループ中でcontinueする時は、できるならなるべく早いタイミングで行うのがいいと考えます。returnには早期returnという考え方がありますが、continuebreakにも同じようなことが言えます。

ループ中ではcontinueできるなら早くしてしまえば、そこから後ろの処理ではcontinueする条件以外なのだと明確にできます。結果として、ループ文などの全体的な見通しが良くなります。

今のプログラムでcontinueをしている箇所は、本当にそこで行わなければならないのか考えてみましょう。もっとシンプルにできるかもしれません。ただ、早くcontinueをしたいがために自然なロジックを捻じ曲げるべきではありませんので、バランス感覚が大事です。

for (int i = 0; i < 10; i++) {
	if (continueできる条件) {
		continue;
	}
 
	// ここから後ろはcontinueできる条件以外だと確定する
}
 
for (int i = 0; i < 10; i++) {
	if (何かの条件1) {
		if (何かの条件2) {
		}
	} else if (何かの条件3) {
		// 本当にここでなければcontinueができないのか?
		if (continueできる条件) {
			continue;
		}
	}
}

4.まとめ

この記事では、Javaにおけるcontinueを紹介してきました。continuefor/while文の中で実行でき、今のループを終了させて次のループとするためのものです。深くネストしたループ文からは、ラベル付きcontinueをすると、親のループ文をcontinueできます。

continueとwhile文の組み合わせで、カウンター変数を使っている場合は注意しましょう。うっかりカウンター変数の加減算を忘れてしまうことがあります。結構見つけづらいバグになることがありますので、変な動きをしている場合は疑ってみてもいいでしょう。

continueはシンプルな機能ですから、他にはあまり注意するところはありません。でも、continueはプログラムを作っていれば日常的に使うものであるからこそ、分かりやすく、読みやすい方法で行いたいものです。何気ないcontinueでも、考えなしに使ってしまうと、結構わかりづらくなってしまうものですよ。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。

コメント

公式アカウントLINE限定!ボールドの内定確率が分かる無料診断実施中
公式アカウントLINE限定!
ボールドの内定確率が分かる無料診断実施中