【初心者用】Javaで『Loto』クラスを作ろう!!
今回は、Javaで『Loto』クラスを作ってみましょう。Lotoは決められた範囲の数字から好きな番号を予測するという「くじ」です。Lotoには「1から43の範囲で6個の数字を選ぶLoto6」と「1から37の範囲で7個の数字を選ぶLoto7」の2種類があります。今回作成するクラスは両方のLotoに対応するものとします。
このクラスを作成するためには、重複しない値をどのようにして判定するかが注意点となります。以前、配列内に重複しないように値を格納する記事を説明していますが、今回はもっと簡単な方法で実現してみます。
まず、この記事は以下のような人を対象としています。
対象者・JavaでLotoを予想するプログラムを自分で作成したい人
・配列内に重複しないように値を格納する方法を知りたい人
・配列内のデータを昇順に並べ替える方法を知りたい人
この記事を読むと、次のようなことが理解できるようになります。
この記事を読むとできること・自分でLotoを予想するプログラムを作成することできる
・配列内に重複しないように値を格納する方法を知ることができる
・配列内のデータを昇順に並び替える方法を知ることができる
Javaの練習として何か自分でクラスを作ってみたいのですが、何か良い練習問題はありますか?
身の回りにある題材が良いと思うので、「Lotoくじ」なんていうのはどうですか?
「Lotoくじ」ですか?
聞いたことがないのですが、どんなくじですか?
自分で好きな番号を予想して、それが一致すると賞金がもらえるというくじです。意外とプログラムの勉強には良い題材なんですよ!!
『Lotoくじ』の仕様
まず、初めには断っておきますが、このプログラムは「Lotoくじを予想してお金を稼ぐ!!」ものではありません。実際にどのような番号がでるのか、前もって予想することは不可能です。あくまでもプログラムの勉強のために作成するだけなので、注意して下さい!!
今回作成するLotoくじクラスは以下の仕様とします。
- Lotoくじを扱う専用のクラスとして作成する(単体で実行はできない)
- コンストラクタにLotoくじの種類(Loto6かLoto7か)を指定する
- 予想の数字は乱数で求める(重複しない)
- Lotoクラスには結果を表示する公開メソッドのみ用意されている
- 結果は昇順に並べ替えられて表示される
アルゴリズム
今回作成するLotoクラスでは、予想する数字は重複しないように求めなければなりません。この重複しないように値を求める方法にはいくつかあります。以前記事でも紹介した「配列内に重複しないように値を求める」方法や、「ArrayListと各種メソッドを使って求める」方法などが考えられます。
プログラムを作成する場合、基本的に求めている結果が得られれば、どれも正解となります。
今回は、ArrayListと各種メソッドを使って求める方法を考えてみます。
ArrayListと各種メソッドを使ったアルゴリズム
JavaのArrayListには「データを並び替えるメソッド」や「データをシャッフルするメソッド」が初めから用意されています。今回はこれらのメソッドを利用します。
重複しないように値を配列に格納する方法を以前の記事で説明していますが、この方法はどの言語でも利用することができる汎用的なアルゴリズムです。一方、今回説明するアルゴリズムはJavaに特化したアルゴリズムになります。メソッドを使うと簡単にプログラムが作成できることを体験してもらいます。
Lotoの予想数字を求めるアルゴリズムの概要は次の通りです。なお、Loto6の場合について説明しますが、Loto7でも若干数字が変わるだけで内容は全く同じになります。
1.Loto6の最終的な予想数字を格納する配列を用意する
まず、予想結果を保存する配列を用意します。Loto6の場合は要素数は6個となります。初期化によって内部には初期値として「0」が格納されています。
2.1から37の数字全てを格納するArrayListを用意する
予想数字を求めるために別途ArrayListを用意します。このArrayListの要素数はLoto6の場合、37となります。要素数は選べる数字の全個数と対応させます。このArrayListには1から37まで数字を順番に格納します。順番に格納しているので重複する数字は存在しません。
3.ArrayList内に格納されている数字をCollections.shuffle()メソッドを使ってシャフルする
1から37まで順番に値が格納されているArrayListをshuffle()メソッドを使ってシャフルします。このメソッドは指定したArrayListに格納されている値を無作為に並び替えるものです。
値を順番に格納するだけであれば配列を使えば良いのですが、今回内部の値をシャッフルするCollections.shuffle()メソッドを利用したいのでArrayListを使っています。通常の配列ではこのshuffle()メソッドに該当するものが用意されていないため利用することができません。
4.シャッフル後のArrayListの最初の6個の数字を1で作成した配列にコピーする
シャッフルしてバラバラになった値の内、最初の6個の値を1で作成済みの配列に順番にコピーします。ArrayListは値をバラバラにシャッフルするためだけに利用しています。
5.コピーした配列の数字をArrays.sort()メソッドを使って昇順に並び替える
これで配列には重複しない値が格納されている状態にできました。このまま表示しても構いませんが、ユーザにとって見やすいようにsort()メソッドを使って昇順に並び替えます。
これで、重複しない予想数字を求めることができました。あらかじめ用意されている便利なメソッドを利用すれば、簡単に求めることができます。
このアルゴリズムの問題点
if文などを使って重複するかをチェックする方法と違って、簡単に予想数字を求めることができるのですが、1つ問題点があります。
例えば、1から10,000までの重複しない数字を3個求めたい場合、このアルゴリズムでは一度1から10,000の値を1個ずつ格納したArrayListを作成しなければなりません。3個の値を求めるために1001個の値を格納するArrayListを作る必要があります。これはメモリの無駄使いとなります。
if文などで求める場合、処理が面倒ですが用意する配列の個数は3個のみとなります。最近のパソコンに搭載されているメモリの容量は大容量なので問題は少ないかもしれませんが、メモリ容量が限られている電子機器などの場合、少しでもメモリの無駄遣いをなくさなければなりません。
したがって、今回説明したアルゴリズムの他に、1個ずつ値が重複するかどうかをチェックするアルゴリズムについても知っておいた方が良いでしょう。
なお、配列に重複しないように値を格納する方法については、以下の記事で説明しています。
Lotoクラスのソースコード
説明したアルゴリズムを利用した実際のコードは以下の通りです。なお、このコードでは処理ごとにメソッドに分割しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package loto; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class Loto { private int[] lotos; // 予想結果を格納する配列 private int limit; // 種類ごとの最大値を格納する変数 // Lotoの種類(6か7)を受け取るコンストラクタ Loto(int type){ // Lotoの種類によって配列を作成(要素数が異なる) lotos = new int[type]; // Lotoの種類によって選択肢の最大値を決める switch(type) { case 6: // Loto6の場合 this.limit = 43; break; case 7: // Loto7の場合 this.limit = 37; } // 予想結果を求め配列に格納 lotos = getNumber(lotos,limit); } // 予想値を求め、結果(配列)を返す非公開メソッド private int[] getNumber(int[] lotos,int limit) { // 可変長配列を作成 List<Integer> numbers = new ArrayList<>(); // ArrayListに1から順番に値を格納 for(int i=0;i<limit;i++) { numbers.add(i+1); } // ArrayList内の値をシャッフル Collections.shuffle(numbers); // Lotoの種類ごとに必要なデータを先頭から配列にコピー for(int i=0;i<lotos.length;i++) { lotos[i]=numbers.get(i); } // 取り出した値(配列)を昇順に並び変える Arrays.sort(lotos); // 求めた予想値(配列)を返す return lotos; } // 予想した結果を表示する公開メソッド public void showLoto() { for(int number : lotos) { System.out.printf("[%2d] ",number); } System.out.println(""); } } |
コードの説明
ここからはコードの説明を簡単にしていきます。
9行目~10行目
このクラスで利用するフィールド(変数)を定義しています。クラス内のフィールドはカプセル化によって保護しなければなりませんのでprivate宣言しています。int型配列のlotosは最終的な予想数字を格納する配列、int型変数のlimitは予想数字の範囲を格納する変数です。
13行目~28行目
引数ありのコンストラクタを定義しています。この引数は作成するインスタンスがLoto6用かLoto7用かを区別するための値です。この値によって最終的な予想数字を格納する配列の要素数と予想で利用する値の範囲が異なります。
なお、引数ありのコンストラクタを定義しているので、引数なしのデフォルトのコンストラクタの定義は消滅しています。ただし、今回は引数なしのコンストラクタは使用しないので別途引数なしのコンストラクタを定義する必要ありません・
15行目
最終的な予想数字を格納する配列lotosを作成しています。作成する要素数はコンストラクタの引数として渡された値によって変わります。Loto6(引数が6)の場合は要素数は6、Loto7(引数が7)の場合は要素数は7となります。また、初期値として「0」が格納されます。
18行目~24行目
Lotoの種類によって選択できる数字の範囲が異なります。したがって、この部分ではLotoの種類に応じた最大値を決め、変数limitに代入しています。Loto6の場合の最大値は「43」、Loto7の場合の最大値は「37」となります。なお、どちらの種類が指定されているかはコンストラクタを作成する時に引数として渡されている値を使って判断します。
27行目
予想数字を求めるメソッドを実行します。今回のサンプルでは予想数字を求めるメソッドは別途ユーザが実行せず、コンストラクタを作成する時に自動的に実行するようにしています。
引数には最終的な予想数字を格納する配列lotosとLotoの種類を格納している変数limitを指定しています。中身が0の配列と選べる予想数字の範囲(最大値)をメソッドに渡し、昇順に並び替えられた配列の結果が戻り値として返されてきます。その配列を再度lotos配列で受け取っています。
31行目~55行目
最終的な予想数字を昇順に並び替えて、その配列を戻り値として返すメソッドを定義しています。なお、このメソッドは外部から直接実行できないようにprivate宣言で非公開メソッドとしています。
34行目
予想数字を全て格納するための作業用ArrayListを作成しています。
37行目~39行目
作成したArrayListに1から順番に予想数字をadd()メソッドを使って格納しています。add()メソッドは()内に格納したい値を指定します。このメソッドを実行すると、ArrayListの最後尾に順番に値が追加されます。for文のカウンタ変数iは0から始まりますが、格納する予想数字は1から始まるので格納する値は「i+1」となります。
42行目
ArrayListに順番に格納されている数字をCollections.shuffle()メソッドを使ってランダムに並び替えています。引数にはシャッフルしたい値が格納されているArrayListを指定します。なお、このメソッドはクラスメソッドなので「Collection.」とクラス名を付けなければなりません。
配列には、shuffule()メソッドのように内部の値をシャッフルできるメソッドが用意されていません。今回はメソッドを使って簡単にプログラムが作成しようとしているので、配列ではなくわざわざArrayListを使っています。
44行目~47行目
シャッフルされたArrayList内のデータのうち、先頭6個の数字を配列lotosに順番に代入しています。ArrayListからデータを取り出すメソッドはget()メソッドとなります。これで最終的な予想数字を配列lotosに格納できました。
50行目
配列lotosにコピーされた予想数字はシャッフルされた状態です。見やすいように昇順に並び替えるために「Arrays.sort()」メソッドを実行します。なお、このメソッドはクラスメソッドなので「Arrays.」とクラス名を付けなければなりません。Arraysクラスは配列に対して実行できる各種メソッドがまとめて定義されています。
53行目
最終的に求めた予想数字が配列に格納されているので、その配列を呼び出し元(コンストラクタの27行目)に返しています。なお、メソッド間での配列の受け渡しは「参照渡し」となります。この「参照渡し」については、いずれ他の記事で説明します。
58行目~63行目
求めた予想数字を順番に標準出力(ディスプレイ)に表示する非公開メソッドを定義しています。
59行目~61行目
拡張for文を利用し、配列内から1個ずつ予想数字を取り出し、ディスプレイに表示しています。表示には書式を設定できる「printf()」メソッドを使っています。「%2d」は「整数のデータを2桁で表示する」という書式です。printf()メソッドはデータを表示しても自動的に改行されません。改行させたい場合は改行させたいところに「%n」と記述します。
62行目
printf()メソッドで結果が1行で表示されているので、全ての予想数字を表示した後に改行しています。
Lotoクラスを利用する確認用のクラス:LotoMain
今回作成したLotoクラスにはエントリポイントとなるmain()メソッドが定義されていないため単独で実行させることができません。そこで、確認用の実行可能なクラスを別途作成します。この確認用クラスの中でLotoクラスのインスタンスをLoto6用とLoto7用に作成し、予想結果を表示させます。
LotoMainクラスのコードは以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package loto; public class LotoMain { public static void main(String[] args) { // Loto6の予想と結果の表示 Loto loto6 = new Loto(6); loto6.showLoto(); // Loto7の予想と結果の表示 Loto loto7 = new Loto(7); loto7.showLoto(); } } |
コードの説明
8行目
Loto6用のインスタンスを作成しています。引数には種類を示す「6」を指定します。
9行目
Lotoクラスに用意されている公開メソッドを実行し、結果を表示しています。
12行目
Loto7用のインスタンスを引数に「7」を指定して作成しています。
13行目
結果を表示する公開メソッドを実行しています。
実行結果
このLotoMainクラスを実行した結果は以下のようになります。実行結果は実行するたびに変わります。
まとめ
今回は、Lotoクラスの作り方について説明しました。色々なアルゴリズムがありますが、今回はJavaに用意されている各種メソッドを使っています。メソッドは言語に依存するため、今回利用したメソッドと同じ機能を提供するものが他の言語には存在しないかも知れません。そのような場合は、面倒でもどの言語でも汎用的に使える昔ながらのif文やfor文を駆使して実現しなければなりません。
どのようなメソッドがあるのかは、是非APIドキュメントなどで日ごろから確認しておくと良いでしょう。
・配列やArrayListには便利なメソッドがあらかじめ用意されている
・配列とArrayListでは利用できるメソッドが若干異なる
・各種メソッドを使うと、面倒なプログラムの記述が不要になる
・昇順に並び替えるためには「sort()」、降順に並び替えるには「reverse()」を使う
Lotoクラスの作成は理解できましたか?
便利なメソッドがたくさんあって、それを利用すると簡単にプログラムを作れることが分かりました!!
メソッドは簡単なんですが、使えない言語もあります。
是非昔ながらの面倒なプログラムを使う方法も一度は確認しておいてください。
配列に重複しないように値を格納する方法については、以下の記事で説明しています。
また、銀行口座クラスの作り方を説明している記事もあります。ぜひ参考にして下さい。