九九の段を作る

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • ループ変数 i を計算式に組み込めば、九九・累積和・配列演算などが書けます
  • 区切り文字を末尾に残さないコツは if (i > 1) result += " "; で先頭以外に空白を入れる
  • 最小例は for (int i = 1; i <= 9; i++) { if (i > 1) sb.append(" "); sb.append(n * i); }

九九の段を作る とは

for ループの応用として、引数 n の段の九九を空白区切りの文字列で組み立てる方法を学びます。

ループ変数を計算に使う

前のレッスンで for 文の基本形を学びました。for (int i = 1; i <= 9; i++) { ... } のように、カウンタ変数 i を 1 から 9 まで動かすあのお馴染みの形です。今回はその一歩先、ループ変数 i を計算に使う というテーマに踏み込みます。

これまで i は「何回ループしたか」を数えるだけの存在でした。でも Java でも他の多くの言語でも、i は普通の int 変数なので、ループの中で n * i のように掛け算したり、配列のインデックス arr[i] として使ったり、自由に計算に組み込めます。この発想ができるようになると、ループで書ける処理の幅が一気に広がります。

「ループ変数はただのカウンタ」と思っていると、for 文の本当の威力を引き出せません。i を計算式の一部として捉えなおすと、九九、累積和、フィボナッチ、配列処理、ありとあらゆる場面で for が武器になります。

このレッスンの題材は、誰もが小学生のときに暗記した 九九 です。「3 の段」「7 の段」のように、引数 n で指定された段の答えを n * 1, n * 2, ..., n * 9 の順で並べた文字列を返すメソッドを書きます。

インデックスと計算結果の違い

まずは課題の構造を分解しましょう。multTable(3) を呼ぶと "3 6 9 12 15 18 21 24 27" を返してほしいわけですが、この出力を for でどう作るか考えます。

  • iインデックス (何番目か、1〜9 の数え上げ)
  • n * i計算結果 (実際に文字列に並ぶ値)

この 2 つを混同しないことがポイントです。i 自体を出力するのではなく、i をかけ算の右辺として使い、その結果 n * i を文字列にしていきます。

Java

for (int i = 1; i <= 9; i++) { int value = n * i; // インデックス i を使って実際の値を計算 // value を文字列に追加する }

int value = n * i; の行が今回の核です。i がループのたびに 1, 2, 3, ..., 9 と変化するので、valuen * 1, n * 2, n * 3, ... と次々に変わっていきます。

i をそのままインデックスとして使うか、n * i のように計算して別の値を作るか。この使い分けが for ループ上達の第一歩です。慣れてくると iarr[i] の添字として使ったり、i % 2 で偶奇を判定したり、i を縦横に展開して二重ループにしたり、応用の幅がぐっと広がります。

動きを追ってみる

multTable(3) を呼んだとき、ループの中で何が起きているかを 1 ステップずつ見てみましょう。

diagram (will load when visible)

n = 3 のときに ivalueresult がどう変わるかを表にすると、次のとおりです。

in * i直前の result追加処理直後の result
13""result += "3""3"
26"3"result += " 6""3 6"
39"3 6"result += " 9""3 6 9"
...............
927"3 6 9 ... 24"result += " 27""3 6 9 ... 27"

この対応関係で意識してほしいのは「i は 1 から 9 まで、valuen * i の結果」という対応関係です。出力に並ぶのは value のほうであって、i そのものではありません。

文字列連結の素直な書き方

ここまでの考え方をそのままコードにすると、次のような形になります。

Java

public class Solution { public static String multTable(int n) { String result = ""; for (int i = 1; i <= 9; i++) { int value = n * i; if (i == 1) { result = result + value; } else { result = result + " " + value; } } return result; } }

if (i == 1) の分岐は、末尾の空白を出さない ための工夫です。素直に result = result + value + " "; と書くと、"3 6 9 12 15 18 21 24 27 " のように最後に余計な空白が 1 つ残ってしまいます。今回のテストは末尾の空白に厳しいので、最初の 1 つは空白なしで、2 つ目以降は前に空白を付ける、という形で組み立てるのが王道です。

「空白を区切り文字として、最初だけ特別扱い」というパターンは、for で文字列を組み立てるときの定番テクニックです。CSV、JSON 配列、SQL の IN 句など、似た構造が至るところで顔を出します。一度覚えるとずっと使えます。

文字列連結が遅い問題と StringBuilder

上のコードは動きはしますが、Java 経験者が見るとちょっと顔をしかめます。理由は String の連結は遅い から、です。

JavaString はイミュータブル (一度作ったら変更できない) という性質があります。result = result + " " + value; と書くと、Java は内部で「新しい String オブジェクトを作って、中身を全部コピーして、古い result を捨てる」という処理をします。ループのたびにこれを繰り返すので、ループ回数が増えるほど効率が悪くなります。

今回のように 9 回だけなら全く問題ありませんが、これが 10000 回、100000 回となると話は別です。そこで Java には専用の道具 StringBuilder が用意されています。

Java

public class Solution { public static String multTable(int n) { StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 9; i++) { if (i > 1) { sb.append(" "); } sb.append(n * i); } return sb.toString(); } }

StringBuilder は中身を書き換えられるバッファです。append で末尾に追加していき、最後に toString()String に変換します。コピーが発生しないので、長いループでも快適に動きます。

入門のうちは String+ 連結で書いて構いません。九九の 9 回程度なら、最近の JVM はコンパイラ最適化で内部的に StringBuilder に置き換えてくれます。ただし「長いループで文字列を作るときは StringBuilder を使う」というキーワードは覚えておきましょう。コードレビューで指摘される定番の話題です。

よくある間違い

九九を for で書くとき、初心者がよくハマるポイントは大きく分けて 3 つあります。

  • 0 から始める / 1 から始めるの混同 — 九九は n * 1 から n * 9 までです。for (int i = 0; i < 9; i++) と書くと i は 0〜8 になり、n * 0 = 0 が先頭に入ってしまいます。今回は i = 1; i <= 9; i++ が正解です
  • 末尾の余計な空白result = result + value + " "; のように書くと、最後の値の後ろにも空白が残ります。テストは "3 6 9 12 15 18 21 24 27" のように末尾に空白なしを期待しているので、if (i == 1) で分岐するか、if (i > 1) で先に空白を入れるパターンを使いましょう
  • in の取り違え — 文字列に並べるのは n * i の結果であって、i そのものではありません。うっかり result = result + i; と書くと "1 2 3 4 5 6 7 8 9" がいつも返ってきてしまい、n = 3 でも n = 7 でも同じ出力になります

もうひとつ、配列を使い慣れている人は int[] arr = new int[9]; のように配列に値を詰めてから String.join(" ", ...) でつなぎたくなりますが、入門段階では素直に for で文字列を組み立てるほうが、ループ変数の使い方が身につきます。

「動けば OK」ではなく「期待される文字列とぴったり一致するか」を意識してください。" " (半角スペース 1 つ) と " " (半角スペース 2 つ) は別物として扱われ、テストは fail します。出力フォーマットの厳密さは、API レスポンスや CSV 生成の現場でもよく問われるテーマです。

この章のポイント

ここまでの要点 ループ変数 in * i のように計算に使う発想を持つ。区切り文字は「先頭以外に前置」で末尾の余計な空白を防ぐ。長いループの文字列組み立ては StringBuilder で高速化。

やってみよう

それでは課題に挑戦してみましょう。手順は次のとおりです。

  1. 結果を貯める変数を用意する (String result = ""; または StringBuilder sb = new StringBuilder();)
  2. for (int i = 1; i <= 9; i++)i を 1 から 9 まで動かす
  3. ループの中で n * i を計算し、空白区切りで結果に追加する
  4. 末尾に余計な空白が残らないように if で先頭だけ特別扱いする (または先に空白を入れる方式にする)
  5. ループを抜けたら結果の文字列を return する

まずは String+ 連結で素直に書いて、テストが pass することを確認してみてください。pass したら、余裕があれば StringBuilder 版に書き換えて、同じテストが通るか試してみましょう。同じ仕様を 2 通りの書き方で実装すると、それぞれの強みと弱みが体で理解できます。

慣れてきたら、引数を int n, int max の 2 つに増やして「n の段を max までかける」ように改造したり、二重ループで 1 の段 から 9 の段 までを全部出力する全段表を作ったり、自由に発展させてみてください。

よくある質問

Q. Java と他言語の文法はどう違いますか?

A. Java は静的型付けで、変数宣言時に型を明示します(int x = 0;)。中括弧でブロックを表し、文末にセミコロンが必要です。Python の動的型付けや JavaScript の柔軟さに慣れていると最初は窮屈ですが、コンパイル時にバグが発見できる安全性がメリットです。

Q. コードが動かないときに最初に見るべき場所は?

A. コンパイルエラーは行番号とエラー種別(cannot find symbol、incompatible types など)が表示されます。実行時例外はスタックトレースの最初の at ... が原因行です。IDE(IntelliJ / VS Code)の警告も丁寧に潰すと、半分のバグは未然に防げます。

Q. Java の習得後に学ぶべき技術は何ですか?

A. 基本構文を抑えたら java-intermediate(コレクション / ジェネリクス / Stream / 例外)に進み、Spring Boot や Web 開発に展開するのが王道です。クラウド時代は Kotlin / Scala への展開も視野に入りますが、まずは Java 標準を固めるのが効率的です。

次のレッスン

次は while 文 で、for ループの応用として、引数 n の段の九九を空白区切りの文字列で組み立てる方法を学びます を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 九九 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 九九 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. クラス名は Solution、メソッド名は multTable、引数は int n 1 つ、戻り値の型は String
  2. for ループを使い、i を 1 から 9 まで動かしながら n * i を組み立てること
  3. 値同士の区切りは半角スペース 1 つ。末尾には余計な空白を残さないこと ("3 6 9 12 15 18 21 24 27" の形式)

入出力例

test-cases.txt

multTable(3)"3 6 9 12 15 18 21 24 27" multTable(7)"7 14 21 28 35 42 49 56 63" multTable(1)"1 2 3 4 5 6 7 8 9" multTable(0)"0 0 0 0 0 0 0 0 0"

ヒント

main.java
main.java
学習モード

メモ

九九の段を作る

⌘S で保存