こんにちは。「Javaを復習する初心者」です。
今回は点数の一覧があったときに、降順で点数の順位を付けるメソッドを考えてみました。
もう少し具体的な状況は以下です。
- int型の配列に点数の一覧が格納されている。(順不同、重複あり)
- その配列内で点数が降順で何位なのかを知りたい。
今回考えたメソッドは、点数と順位のペアのMapを返却するというものです。点数に重複があるので、その部分に工夫が必要です。
以下、考えてみたことを少しずつ解説しながら書いていきます。最終的なメソッドを知りたい人は最後の方を読むと良いと思います。
引数と戻り値
Rankingというクラスを作って、staticメソッドを作ろうと考えました。以下のような感じです。
- メソッド名
- getRankingMap
- 引数
- int points[]
- 戻り値
- Map<Integer, Integer>
引数は点数が格納されている配列を想定しています。また、点数は順不同で重複ありで格納されているという想定です。
戻り値は点数をkeyにして、順位を値に持つMapにしようと考えました。get()メソッドに点数を渡すと、その点数の順位を得ることが出来るという考えです。
降順に並べる
java.util.Arraysクラスにはsortメソッドがあります。これは与えられた配列を昇順に並べ替えるメソッドです。このメソッドを使って、そのあとは配列の順番を逆にすることで、降順にできました。
メソッドの前半は以下のようになりました。
ソース
// ソート(昇順)
int len = points.length;
int temp[] = new int[len];
for (int i = 0; i < len; i++) {
temp[i] = points[i];
}
Arrays.sort(temp);
// 降順にする
int pointsDesc[] = new int[len];
for (int i = 0; i < len; i++) {
pointsDesc[i] = temp[len - 1 - i];
}
Arraysのsort()メソッドは与えられた配列自体を並べ替えてしまうため、引数を直接このメソッドに渡すのは避けました。降順にする箇所は単純に、配列の後ろから格納しなおしています。
ランク付け
ランクについては、通し番号を使うことを考えました。以下のように、降順に並べたときに、ひとつ上の点数と異なるなら、通し番号が順位になります。
点数 | 通し番号 | 順位 |
---|---|---|
9 | 1 | 1 |
8 | 2 | 2 |
8 | 3 | 2 |
7 | 4 | 4 |
通し番号N番の人の点数について、以下のようにすれば良いことになります。(Nは2以上)
- ひとつ前の番号の人の点数と同じならば、前の人と同じランクになる。
- それ以外の場合、通し番号がランクになる。
このように考えた結果、以下のような実装になりました。
ソース
// ランク付け
Map<Integer, Integer> map = new LinkedHashMap<>();
int rank = 1;
map.put(pointsDesc[0], rank);
for (int i = 1; i < len; i++) {
if (pointsDesc[i] != pointsDesc[i-1]) {
// 点数が前の人と違うなら、通し番号を設定
rank = i + 1;
}
map.put(pointsDesc[i], rank);
}
変数mapに点数とランクのペアを格納していくというやり方です。通し番号1番(配列の要素番号0)に対する点数はrankが1位なので、無条件にmapに格納しています。
変数rankは、通し番号がひとつ前の人との比較で決めています。点数が異なるならば、rankは通し番号になり、そうでないならば、rankは変化させてません。
これでランキングのMapができたことになります。
メソッド全体
メソッド全体は以下のようになりました。
ソース
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
public class Ranking {
static Map<Integer, Integer> getRankingMap(int points[]) {
// ソート(昇順)
int len = points.length;
int temp[] = new int[len];
for (int i = 0; i < len; i++) {
temp[i] = points[i];
}
Arrays.sort(temp);
// 降順にする
int pointsDesc[] = new int[len];
for (int i = 0; i < len; i++) {
pointsDesc[i] = temp[len - 1 - i];
}
// ランク付け
Map<Integer, Integer> map = new LinkedHashMap<>();
int rank = 1;
map.put(pointsDesc[0], rank);
for (int i = 1; i < len; i++) {
if (pointsDesc[i] != pointsDesc[i-1]) {
// 点数が前の人と違うなら、通し番号を設定
rank = i + 1;
}
map.put(pointsDesc[i], rank);
}
return map;
}
}
戻り値は実際にはLinkedHashMapクラスのインスタンスです。降順にkeyを格納しているので、その順序が保たれた方が呼び出し元にとって便利かと思ったのですが、好みの問題かもしれません。
以下のようなメソッドで動きをテストしてみました。
テスト
int points[] = {10, 12, 9, 10, 8, 7, 7, 7, 6};
Map<Integer, Integer> rankingMap = Ranking.getRankingMap(points);
for (int i = 0; i < points.length; i++) {
int point = points[i];
System.out.println((i + 1) + "番目; " + point + "点; " + rankingMap.get(point) + "位.");
}
System.out.println("-参考-");
for (Integer point : rankingMap.keySet()) {
System.out.println(rankingMap.get(point) + "位:" + point + "点");
}
結果
1番目; 10点; 2位.
2番目; 12点; 1位.
3番目; 9点; 4位.
4番目; 10点; 2位.
5番目; 8点; 5位.
6番目; 7点; 6位.
7番目; 7点; 6位.
8番目; 7点; 6位.
9番目; 6点; 9位.
-参考-
1位:12点
2位:10点
4位:9点
5位:8点
6位:7点
9位:6点
結果は上記のようになりました。10点が二人いるため、次の9点は4位になっています。