こんにちは、運営者のハックです。
今回はデイトラJava Silver対策編「第4章の対策」の学習記録のうち前半部分を紹介します。
第4章 制御構造
第4章では主にループ(for文、拡張for文、while文、do-while文)、そしてジャンプ(break文、continue文、return文)について解いていきます。
今回は前半部分であるループのうちfor文、while文、do-while文について解きました。
引き続き解説の参考と例題の引用は【黒本】と呼ばれる「徹底攻略Java SE 11 Silver問題集」から行っていきます。
while文, do-while文
Javaのwhile文とdo-while文は、特定の条件が満たされている間、コードブロックを繰り返し実行するために使用します。これらはループ構造の一部であり、プログラム内で反復処理を行う際に便利です。
while文
while文は、ループの開始時に条件を評価します。条件がtrue
であれば、ループ内のコードが実行されます。条件がfalseになるとループから抜け出します。ループの開始前に条件がチェックされるため、条件が初めからfalseの場合、コードブロックは一度も実行されません。
int i = 0;
while (i < 5) {
System.out.println("i = " + i);
i++; // インクリメントしていることを忘れないように
}
上記のコードでは、i
が5未満の間、iの値を出力し続けます。iはループのたびに1ずつ増加し、iが5に達するとループが終了します。
do-while文
do-while文は、while文と似ていますが、最低一回はループ内のコードを実行し、その後で条件を評価します。少なくとも一度はコードブロックが実行された後で条件がチェックされるため、条件の真偽に関わらず最初の実行は保証されます。なお、条件式の後ろにはセミコロンが必要です。
int i = 0;
do {
System.out.println("i = " + i);
i++;
} while (i < 5);
この例では、i
の初期値が0であるため、条件式の評価前にi = 0が出力されます。その後、iがインクリメントされ、条件i < 5が再評価されます。このプロセスがi
が5になるまで続きます。
現場ではwhile文,do-while文は使用しない
ねーねー、デイトラJavaコース中級編まで終わってるけどさぁ、while文とdo-while文って習ってない気がするのにゃ。
現場ではwhile文,do-while文は「無限ループ」が発生する可能性があるため使用しないそうです。なのでデイトラでは現場で使用していないwhile文,do-while文は扱っていないそうです。
Javaにおけるwhile文とdo-while文で無限ループが発生するのは、ループを抜け出す条件がfalseにならない場合です。これは、ループの終了条件が満たされずに繰り返しが永遠に続く状態を指します。
例えば以下のようなコードは「この行は無限に出力されます」という文字列を無限にコンソールに出力し続けます。
while (true) {
System.out.println("この行は無限に出力されます");
}
無限ループはプログラムをフリーズさせたり、意図しない高CPU使用率を引き起こす原因となるため、通常は避けるべきですが、特定の用途(例えば、サーバーのリクエスト待機など)で意図的に使用されることもあります。
現場では使用されないwhile文,do-while文ですがJavaSilvreの問題としては出題されます。
次は実際に例題を解いてみましょう。
次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)
1. public class Main{ 2. public static void main(String[] args) { 3. int a = 0; 4. while(a < 5) 5. do 6. a++; 7. System.out.println(a); 8. while(true); 9. } 10. }
A. 012が表示される。
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. 012が5回表示される。
C. 何も表示されない。
D. 無限ループになる。
E. コンパイルエラーが発生する。
F. 実行時に例外がスローされる。
…あにゃ?while文の中が中括弧で閉じられていないのにゃ。
前の記事でも中括弧で閉じられていない場合直後の一文しか実行しないって言っていた気がするのにゃ。そうすると、a<5の間はa++を実行するってことかにゃ?でもa++の下にもSystem.out.println(a);という文があるのにゃ。
あと、その下にもwhile文があるのにゃ。下のwhile文には条件式が書いてなくて…
何かこのコードおかしくないかにゃ?
ねこ奈の予感はだいたい当たってますよ、正解は選択肢Eです。
if文やif-else文と同じようにwhile文やdo-while文でも中括弧を省略できます。中括弧を省略した場合、1つの文だけが繰り返し処理として扱われます。
ただし、do-while文で中括弧を省略した場合、doの後ろには1つしか文を記述できません。中括弧を省略したのに文を複数行記述するとコンパイルエラーが発生します。
例題のコードは中括弧を省略しているにもかかわらず文を複数記述しているためコンパイルエラーが発生します。
for文
for文についてはJavaコース初級 基礎編Day9「繰り返し、配列、拡張for文、List、Map」の記事で解説していますが、今回はさらに文法やルールについて深堀して解説します。
for文の文法やルールをさらに深堀りして学習!
for文内の「初期化文」「条件文」「更新文」の文法やルールについて、深堀りして解説します。
初期化文(Initialization)
int i = 1, j = 0;
- 目的:ループカウンタの初期値設定に使用します。
- 実行タイミング:for文が初めて実行される前に一度だけ実行されます。
- 変数のルール:同時に複数の変数を宣言、初期化ができますが、同じ型である必要があります。
- 変数はブロック内でのみ有効:宣言した変数はfor文の外では使用できません。
条件文(Condition)
i <= 5 && j < 10;
- 目的:ループの継続条件を指定します。
- 複数の条件:論理演算子を使用して複数の条件を記述可能ですが、カンマ「,」で区切ることはできません。
- 実行タイミング:各イテレーションの開始前に評価されます。この条件がtrueの場合、ループ内のコードブロックが実行されます。falseの場合、ループから抜け出します。
更新文(Update)
i++, period()
- 目的:ループカウンタの更新を担います。
- 複数の条件:カンマ「,」で区切って複数の処理を列挙することが可能です。
- 実行タイミング:各イテレーションの終わりに実行されます。初期化文→条件文→繰り返し処理→更新文→条件文…という実行の順番になります。
for文の無限ループと二重ループ
デイトラJavaコースの講義では詳しく解説していないfor文の二重ループと無限ループについて、今回は深堀りして解説します。
for文の無限ループ
無限ループは、ループの終了条件が常に真(true)と評価されるため、ループが終了しない状態を指します。これは時に意図的に使用されることもありますが、多くの場合はプログラムのバグによるものです。
public class Main {
public static void main(String[] args) {
for (int i = 0; true ;i++ ) {
System.out.println("このメッセージは永遠に出力されます");
}
}
}
この例では固定値であるリテラル「true」を記述することによって常に条件に合致していることになり、プログラムは特定の条件で停止することなくずっと同じブロックのコードを実行し続けます。
他にもfor文は条件式と更新式は省略可能ですが、条件式を省略した場合「break」を使用しない限り無限ループとなります。
for文の二重ループ
二重ループは、一つのループの中に別のループが入っている状態を指します。これは、多次元の配列を処理する時や、複数の要素に対する繰り返し処理を組み合わせる時に使われます。
public class Main {
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int i = 0; i < matrix.length; i++) { // 外側のループが行を管理
for (int j = 0; j < matrix[i].length; j++) { // 内側のループが列を管理
System.out.print(matrix[i][j] + " ");
}
System.out.println(); // 新しい行で出力を始める
}
}
}
コードの流れを詳しく説明します。
1.2次元配列の初期化
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
ここで matrix は3×3の配列を定義しています。配列の各行は {}
内に数字のリストとして格納されています。
2.外側のforループ
for (int i = 0; i < matrix.length; i++){
このループは matrix 配列の各「行」に対して繰り返されます。matrix.length は行列の行数、ここでは3です(3行あるため)。
3.内側のforループ
for (int j = 0; j < matrix[i].length; j++) {
内側のループは、選択された行の各「列」に対して繰り返されます。matrix[i].length は現在選択されている行の列数を表します。この場合、すべての行に3つの要素があるため、この値は3です。
4.要素の出力
System.out.print(matrix[i][j] + " ");
ここで、matrix[i][j] は i 行目の j 列目の要素を指します。この命令は選択された要素をスペースとともに出力します。
5.新しい行での出力開始
System.out.println();
各行の要素が出力された後、System.out.println(); は新しい行にカーソルを移動させます。これにより、次の行の要素は新しい行に出力されることになります。
出力結果
1 2 3
4 5 6
7 8 9
各数字はその行と列に対応した matrix 配列の要素を表しています。
数字の後のスペースは System.out.print(matrix[i][j] + ” “); によるもので、各要素を区切っています。
各行が終わるごとに System.out.println(); が呼ばれるため、新しい行が始まるたびに次の行の数字が新しい行に表示されます。
このように二重ループを使うことで、2次元配列の全要素を綺麗に行と列に沿って出力することができます。
実際にfor文の二重ループの例題を見てみましょう。
次のプログラムを実行し,、コンソールに「10」と表示したい。6行目の空欄に入るコードとして、正しいものを選びなさい。(1つ選択)
1. public class Main { 2. public static void main(String[] args) { 3. int array[][] = new int[][]{{1,2},{2,3,4}}; 4. int total = 0; 5. for(int i = 0; i< array.length; i++){ 6. for([ 空欄 ]){ 7. total += array[i][j]; 8. } 9. } 10. System.out.println(total); 11. } 12. }
A. int j = 0; j< array[i].length; j++
「徹底攻略 Java SE 11 Silver 問題集」より抜粋
B. int j = 0; j< array[j].length; j++
C. int j = i; j< array[i].length; j++
D. int j = i; j< array[j].length; j++
ぎゃにゃあ…
これって選択肢ごとに場合分けして考えないといけないのにゃ?
そのようですね。ひとつづつ場合分けして考えてみましょう。
A. int j = 0; j< array[i].length; j++ のケース
3. int array[][] = new int[][]{{1,2},{2,3,4}};
4. int total = 0;
5. for(int i = 0; i< array.length; i++){
6. for([int j = 0; j< array[i].length; j++ ]){
7. total += array[i][j];
8. }
9. }
10. System.out.println(total);
11. }
このケースの場合iの値は0~1、jの値は0~2となります。
i=0の時、j=0…といった感じに順番に当てはめた表が以下のとおりです。
変数iの値 | 変数jの値 | 要素の値 | 条件式 | 変数totalの値 |
---|---|---|---|---|
0 | 0 | 1 | j < 2 | 1 |
0 | 1 | 2 | j < 2 | 3 |
1 | 0 | 2 | j < 3 | 5 |
1 | 1 | 3 | j < 3 | 8 |
1 | 2 | 4 | j < 3 | 12 |
え~と、「total += array[i][j];」で二次元配列の各要素(数字)を合計するから、
1+2+2+3+4=12となるのにゃ。だから選択肢Aは誤りだにゃ!
このような感じで場合分けして考えましょう。
次は選択肢Bを確認します。
B. int j = 0; j< array[j].length; j++の場合
3. int array[][] = new int[][]{{1,2},{2,3,4}};
4. int total = 0;
5. for(int i = 0; i< array.length; i++){
6. for([int j = 0; j< array[j].length; j++ ]){
7. total += array[i][j];
8. }
9. }
10. System.out.println(total);
11. }
同じようにBも場合分けして考えていきます。選択肢Aと違ってBは「j< array[j]」となっており、内側のループが一度回るたびに条件文が変化します。表で場合分けすると以下のようになり、例外がスローされます。
変数iの値 | 変数jの値 | 要素の値 | 条件式 | 変数totalの値 |
---|---|---|---|---|
0 | 0 | 1 | j < 2 | 1 |
0 | 1 | 2 | j < 3 | 3 |
0 | 2 | なし | j < ??? |
配列{1,2}には0~1番目までの数列しか入っていないのにゃ!
2番目の数列には何も入っていないから例外がスローされるんだにゃ。
同様に「j< array[j]」の条件式となっている選択肢Dも誤りとなります。
C. int j = i; j< array[i].length; j++の場合
Cは初期化文が「int j = i」と変化します。同じように表で場合分けします。
変数iの値 | 変数jの値 | 要素の値 | 条件式 | 変数totalの値 |
---|---|---|---|---|
0 | 0 | 1 | j < 2 | 1 |
0 | 1 | 2 | j < 2 | 3 |
1 | 1 | 3 | j < 3 | 6 |
1 | 2 | 4 | j < 3 | 10 |
jが1から始まっているのにゃ!
「int j = i」って初期化しているからかにゃ。
それに伴い取り出す要素の値も変化します。
i = 1の時 j = 1なので、配列の0番目にある「2」は取り出さず、1番目にある「3」から取り出します。
1+2+3+4=10となるため、選択肢Cが正解となります。
続きは後半で
今回は前半部分であるループのうちfor文、while文、do-while文について深掘りした内容の紹介と、実際の例題を解説しました。後半は「拡張for文、ジャンプ(break文、continue文、return文)」について解説します。
今回のfor文の二重ループ問題は今までで一番苦戦しました。
書籍を読んでも理解できない場合はIntelliJで実際にコードを記述し実行しています。
それでも理解できない場合はChatGPTにコード文ごとに分解して「なぜこのように出力されるのか」が完全に理解できるまで質問しています。
以上で今回の学習記録を終えます。
ここまでご覧いただきありがとうございました。
コメント