
6日でできる 新Java入門|クイズ処理の流れ
学んだ知識をひとつにつなげて、Javaで動くクイズゲームの流れを自分の手で組み立てよう
これまで学んできたファイル読み込み、ArrayList、ランダム数、キーボード入力は、それぞれ単体でも大事な技術ですが、実際のプログラムではそれらを組み合わせて使う場面がとても多いです。今回の「クイズ処理の流れ」は、まさにその練習にぴったりの題材です。
都道府県名を見て県庁所在地を答えるクイズは、見た目はシンプルですが、中ではいろいろな処理が連携しています。ファイルから問題データを読み込む処理、ArrayList にデータを整理して保存する処理、Random を使って問題や選択肢をばらけさせる処理、そしてキーボードから入力を受けて正誤判定をする処理です。これらが順番につながることで、ひとつの「動くアプリ」になります。
つまり今回は、文法を個別に覚える段階から一歩進んで、プログラム全体の流れを理解することが大きなテーマです。部分ごとの知識をつなげて見られるようになると、Javaでできることが一気に広がってきます。
クイズ処理の流れは「部品の連携」でできている
クイズゲームは、いきなり問題を出しているように見えて、実際にはいくつかの処理が順番に動いています。まずは全体像をつかんでおくと、このあとコードを読むときにかなり理解しやすくなります。
| 処理の段階 | 何をしているか |
|---|---|
| 準備 | スコアやリスト、Random を用意する |
| 読み込み | prefectures.txt から都道府県と県庁所在地を読み込む |
| 出題 | ランダムに問題を選ぶ |
| 選択肢作成 | 正解と誤答3つを並べて4択にする |
| 入力受付 | キーボードから 1~4 を受け取る |
| 判定 | 正解か不正解かをチェックする |
| 終了処理 | 10問終わったら得点を表示する |
この流れを見ると、クイズ処理は特別な魔法ではなく、これまで学んだ基本技術を順番につないだものだと分かります。大切なのは、ひとつひとつの命令よりも、「どの順番で、何のために動いているか」を意識することです。
ゲーム開始前に何を準備しているのか
クイズを始める前には、まず必要な変数やデータ構造をそろえます。
この準備がしっかりしていないと、後の処理がうまく動きません。
| 準備するもの | 役割 |
|---|---|
| int型の score | 正解した回数を記録する |
| ArrayList prefectures | 都道府県名を保存する |
| ArrayList capitals | 県庁所在地を保存する |
| Random rand | ランダム出題や選択肢生成に使う |
ここで注目したいのは、都道府県名と県庁所在地を別々の ArrayList に入れていることです。
これは、同じ添字どうしが対応するようにするためです。
たとえば、
| 添字 | prefectures | capitals |
|---|---|---|
| 0 | 北海道 | 札幌 |
| 1 | 青森県 | 青森 |
| 2 | 岩手県 | 盛岡 |
このようにしておけば、prefectures のある位置を選んだとき、同じ位置の capitals を見れば正解が分かります。
この「添字で対応させる考え方」は、今回のプログラムの中心です。
prefectures.txt を読み込む意味
クイズの問題データをソースコードに直接ずらっと書くこともできますが、今回は prefectures.txt というテキストファイルから読み込みます。
こうすることで、問題データだけを差し替えたり、後から増やしたりしやすくなります。
ファイル名:prefectures.txt
北海道,札幌
青森県,青森
岩手県,盛岡
宮城県,仙台
秋田県,秋田
山形県,山形
福島県,福島
茨城県,水戸
栃木県,宇都宮
群馬県,前橋
埼玉県,さいたま
千葉県,千葉
東京都,東京
神奈川県,横浜
新潟県,新潟
富山県,富山
石川県,金沢
福井県,福井
山梨県,甲府
長野県,長野
岐阜県,岐阜
静岡県,静岡
愛知県,名古屋
三重県,津
滋賀県,大津
京都府,京都
大阪府,大阪
兵庫県,神戸
奈良県,奈良
和歌山県,和歌山
鳥取県,鳥取
島根県,松江
岡山県,岡山
広島県,広島
山口県,山口
徳島県,徳島
香川県,高松
愛媛県,松山
高知県,高知
福岡県,福岡
佐賀県,佐賀
長崎県,長崎
熊本県,熊本
大分県,大分
宮崎県,宮崎
鹿児島県,鹿児島
沖縄県,那覇Eclipse の場合は、この prefectures.txt をプロジェクト直下に置いておきます。
1行の形式は、都道府県,県庁所在地 です。
このカンマ区切りを使って、1行を2つに分けてリストへ入れていきます。
1行ずつ読み込んで ArrayList に分ける処理
ファイルを読む部分では、BufferedReader と FileReader を使っています。
流れとしては、1行読み込む、カンマの位置を探す、前半と後半に分ける、という形です。
BufferedReader br = new BufferedReader(new FileReader("prefectures.txt"));
String line;
while ((line = br.readLine()) != null) {
int idx = line.indexOf(',');
prefectures.add(line.substring(0, idx));
capitals.add(line.substring(idx + 1));
}
br.close();この処理でやっていることを分解すると、次のようになります。
| コード | 役割 |
|---|---|
| br.readLine() | ファイルを1行ずつ読む |
| line.indexOf(',') | カンマの位置を調べる |
| line.substring(0, idx) | カンマより前を取り出す |
| line.substring(idx + 1) | カンマより後ろを取り出す |
| prefectures.add(...) | 都道府県名を追加する |
| capitals.add(...) | 県庁所在地を追加する |
ここで大事なのは、都道府県名と県庁所在地を必ず同じ順番で追加することです。
順番がずれると、たとえば 北海道 に対して 盛岡 が対応してしまうような不具合が起きます。
クイズの出題はどう作られているのか
問題を出すときは、prefectures の中からランダムに1件を選びます。
そのときに使うのが Random の nextInt(リストのサイズ) です。
int qIdx = rand.nextInt(prefectures.size());
String questionPref = prefectures.remove(qIdx);
String correctAns = capitals.remove(qIdx);ここでは qIdx にランダムな添字が入ります。
その添字を使って、問題となる都道府県と正解の県庁所在地をセットで取り出しています。
しかも get ではなく remove を使っているのがポイントです。
remove を使うと、その要素はリストから消えます。つまり、一度出題した都道府県はもう出てきません。
この仕組みにはとても大きな意味があります。
| remove を使う理由 | 効果 |
|---|---|
| 出題済みの問題を消す | 同じ問題の重複出題を防げる |
| 同じ添字で両方消す | 都道府県と県庁所在地の対応関係が崩れない |
このように、問題の選択は「ランダムで取り出す」だけでなく、「使ったものを次回以降は除外する」ところまで含めて設計されています。
選択肢はどうやって作っているのか
4択クイズでは、正解1つだけでは成り立ちません。
それっぽい誤答も必要です。今回のプログラムでは、他の県庁所在地を3つランダムに選んで選択肢にしています。
ArrayList<String> choices = new ArrayList<String>();
ArrayList<String> backupCapitals = new ArrayList<String>(capitals);
for (int j = 0; j < 3; j++) {
int cIdx = rand.nextInt(backupCapitals.size());
choices.add(backupCapitals.remove(cIdx));
}ここで面白いのは、直接 capitals から削除するのではなく、backupCapitals を作っていることです。
つまり、本体の capitals はそのまま残しつつ、選択肢作成用に一時的なコピーを使っています。
この方法のメリットは、誤答候補を選ぶときだけ安全に remove を使えることです。
| 変数名 | 役割 |
|---|---|
| capitals | 本来の県庁所在地データ |
| backupCapitals | 誤答候補を選ぶための一時コピー |
| choices | 実際に表示する4択のリスト |
この考え方は実務でもよく使います。
元データを壊したくないときは、コピーを作ってから加工する、という考え方です。
正解の位置もランダムにしている
誤答を3つ入れただけでは、まだ選択肢は完成していません。
最後に正解を choices のどこかにランダムで差し込みます。
int insertIdx = rand.nextInt(choices.size() + 1);
choices.add(insertIdx, correctAns);choices.size() が 3 の状態でこれを実行すると、0~3 のどこかに正解が入ります。
つまり、1番目に正解が来ることもあれば、4番目に来ることもあります。
これはクイズとしてとても大事です。
もし毎回3番目が正解だったら、内容を考えなくても当てやすくなってしまいます。
正解の場所もランダムにすることで、クイズとしての自然さと面白さが増します。
入力受付の処理で見ておきたいこと
このプログラムでは、キーボード入力を受けて 1~4 の番号を選ばせています。
ここは初心者の方が少し読みにくく感じやすい部分ですが、やっていることを分けて見ると分かりやすいです。
byte[] b = new byte[128];
int answer;
while (true) {
System.out.print("==> ");
System.in.read(b);
try {
answer = Integer.parseInt((new String(b)).trim().substring(0, 1));
if (answer >= 1 && answer <= 4) {
break;
} else {
System.out.println("1~4の数字で選んでください。");
}
} catch (Exception e) {
System.out.println("数字を入力してください。");
}
}
answer--;この処理の役割を表で整理すると、こうなります。
| 処理 | 内容 |
|---|---|
| System.in.read(b) | 入力を受け取る |
| new String(b).trim() | 余計な空白や改行を除く |
| substring(0, 1) | 最初の1文字を取り出す |
| Integer.parseInt(...) | 数字に変換する |
| if (answer >= 1 && answer <= 4) | 1~4 の範囲か確認する |
| answer-- | 1~4 を 0~3 の添字に合わせる |
ここで answer-- している理由は、選択肢の表示番号と ArrayList の添字を合わせるためです。
画面では 1, 2, 3, 4 と表示していますが、リストの添字は 0, 1, 2, 3 です。
その差を埋めるために 1 引いています。
このように、見た目の番号と内部の番号は違うことがある、というのはプログラミングでよく出てくる考え方です。
正誤判定はとてもシンプル
入力された答えが insertIdx と同じなら正解です。
つまり、正解を差し込んだ位置と、ユーザーが選んだ位置を比べています。
if (answer == insertIdx) {
score++;
System.out.println("正解です!");
} else {
System.out.println("不正解。正解は「" + correctAns + "」です。");
}この判定がシンプルなのは、前の段階できちんと選択肢を組み立てているからです。
設計が整理されていると、判定処理も分かりやすくなります。
また、正解したら score++ で得点を1増やしています。
このように、正誤判定とスコア管理が自然につながっているところも、全体の流れとして見ておきたい部分です。
10問で終了する流れ
クイズは for 文で 10 回くり返しています。
for (int i = 1; i <= 10; i++) {
...
}このため、1問目から10問目まで順番に出題されます。
そしてループが終わったら、最後に得点を表示します。
System.out.println("全10問終了!あなたの得点は " + score + " 点です。");この構造を見ると、ゲーム全体が「10問出題して終了する」というルールで作られていることがよく分かります。
今後、5問モードや20問モードを作りたい場合は、この 10 を変更すれば基本の動きは変えられます。
lesson24_1.java 全体を確認しよう
ここでサンプルプログラム全体を、あらためてそのまま確認しておきましょう。
ファイル名:lesson24_1.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Random;
public class lesson24_1 {
public static void main(String[] args) throws Exception {
// スコア
int score = 0;
// 都道府県名と県庁所在地のリスト
ArrayList<String> prefectures = new ArrayList<String>();
ArrayList<String> capitals = new ArrayList<String>();
// ランダム生成器
Random rand = new Random();
// ファイルから読み込み
BufferedReader br = new BufferedReader(new FileReader("prefectures.txt"));
String line;
while ((line = br.readLine()) != null) {
int idx = line.indexOf(',');
prefectures.add(line.substring(0, idx));
capitals.add(line.substring(idx + 1));
}
br.close();
// 10問出題
for (int i = 1; i <= 10; i++) {
int qIdx = rand.nextInt(prefectures.size());
String questionPref = prefectures.remove(qIdx);
String correctAns = capitals.remove(qIdx);
// 選択肢リスト作成
ArrayList<String> choices = new ArrayList<String>();
ArrayList<String> backupCapitals = new ArrayList<String>(capitals); // 一時的バックアップ
// 他の3つを選択肢に追加
for (int j = 0; j < 3; j++) {
int cIdx = rand.nextInt(backupCapitals.size());
choices.add(backupCapitals.remove(cIdx));
}
// 正解の挿入位置をランダムで決める
int insertIdx = rand.nextInt(choices.size() + 1);
choices.add(insertIdx, correctAns);
// 問題・選択肢の表示
System.out.println(i + "問目:次の都道府県の県庁所在地はどれ?");
System.out.println("「" + questionPref + "」");
for (int j = 0; j < choices.size(); j++) {
System.out.println((j + 1) + ":" + choices.get(j));
}
// 入力受付とチェック
byte[] b = new byte[128];
int answer;
while (true) {
System.out.print("==> ");
System.in.read(b);
try {
answer = Integer.parseInt((new String(b)).trim().substring(0, 1));
if (answer >= 1 && answer <= 4) {
break;
} else {
System.out.println("1~4の数字で選んでください。");
}
} catch (Exception e) {
System.out.println("数字を入力してください。");
}
}
answer--;
// 判定
if (answer == insertIdx) {
score++;
System.out.println("正解です!");
} else {
System.out.println("不正解。正解は「" + correctAns + "」です。");
}
System.out.println();
}
// ゲーム終了
System.out.println("全10問終了!あなたの得点は " + score + " 点です。");
}
}実行例から見えること
実行例は次のようになります。
1問目:次の都道府県の県庁所在地はどれ?
「長野県」
1:甲府
2:松山
3:長野
4:津
==> 3
正解です!
2問目:次の都道府県の県庁所在地はどれ?
「群馬県」
1:高知
2:前橋
3:松江
4:秋田
==> 2
正解です!
・・・
全10問終了!あなたの得点は 9 点です。この実行例から分かるのは、問題も選択肢も毎回固定ではないということです。
つまり、同じプログラムでも Random によって実行のたびに変化が生まれています。
この変化があることで、ただの確認問題ではなく、ちゃんと「遊べるクイズ」になっています。
このプログラムの良いところ
lesson24_1.java は、学習用のサンプルとしてとても良くできています。
理由は、Javaの基本技術が実際のアプリの流れの中で使われているからです。
学んだ内容が自然につながっている
- ファイル読み込み
- 文字列の分割
- ArrayList の追加と削除
- Random によるランダム処理
- キーボード入力
- 条件分岐
- ループ
- スコア管理
これらがバラバラに出てくるのではなく、「クイズゲームを動かす」というひとつの目的の中で使われています。
そのため、個々の文法の意味だけでなく、何のために使うのかが見えやすいです。
拡張しやすい形になっている
このプログラムは、今後の改造も考えやすいです。たとえば次のような発展ができます。
| 拡張案 | 内容 |
|---|---|
| 難易度変更 | 問題数や選択肢数を変える |
| ランキング機能 | 得点をファイルへ保存する |
| ジャンル追加 | 都道府県以外の問題も出せるようにする |
| 制限時間 | 一定時間以内に答えさせる |
| 再挑戦機能 | もう一度遊べるメニューを作る |
こうした拡張がしやすいのは、基本の流れが整理されているからです。
図:クイズ処理の全体フロー
↓クリックすると拡大表示されます。

この図が示していること
クイズゲームが、準備、読み込み、出題、入力、判定、終了という流れで動いていることを表しています。
この図から分かること
クイズ処理はひとつの命令だけでできているのではなく、複数の基本技術が順番につながって成り立っていることが分かります。
図:1問分の出題から判定までの流れ
↓クリックすると拡大表示されます。

この図が示していること
1問分のクイズが、ランダム出題、選択肢作成、入力受付、正誤判定という順に処理される流れを表しています。
この図から分かること
クイズは単に問題を表示しているだけではなく、裏側でリスト操作、ランダム処理、入力判定が連携して動いていることが分かります。
注意して見ておきたいポイント
このプログラムを読むときは、次の点を意識すると理解が深まります。
添字の対応関係を崩さないこと
prefectures と capitals は、同じ添字どうしがセットです。
そのため、片方だけ消したり、順番がずれたりすると正しい組み合わせが壊れます。
データ数とのバランスを考えること
今回は 47件のデータから 10問を出すので余裕があります。
でも、問題数が多すぎたり選択肢数が多すぎたりすると、誤答候補が足りなくなることがあります。
文字コードやファイルの置き場所にも気をつけること
日本語を含むテキストファイルなので、UTF-8 で保存しておくほうが安心です。
また、prefectures.txt の場所が違うと FileNotFoundException が出ることがあります。
このテーマで身につく力
「クイズ処理の流れ」を理解すると、Javaの文法を点ではなく線で見られるようになります。
それぞれの知識が、実際のプログラムの中でどうつながるのかが分かるようになるからです。
今回の lesson24_1.java は、単なる練習コードではなく、実用的なプログラムの入口としてとてもよい題材です。
ファイル読み込み、ArrayList、Random、入力処理、条件分岐、繰り返し。これらをひとつの流れとして見られるようになると、今後は自分でオリジナルのクイズやゲーム風プログラムも作りやすくなります。
次の一歩としては、制限時間を付けたり、正解数に応じてランクを表示したり、ジャンル別クイズにしたりするのも面白いです。
今回の流れが理解できれば、そうした機能追加にもかなり挑戦しやすくなります。
