テーマ
オブジェクトを格納した、ArrayListをソートする方法を学ぼう
お題
以下のような点数と名前のフィールドを持つStudentクラスがある。
class Student {
String name;
int score;
Student(String name,int score){
this.name=name;
this.score=score;
}
void showStatus() {
System.out.printf("名前:%s,点数:%d%n", this.name,this.score);
}
}
アプリケーションクラスでこのクラスのインスタンスをいくつか格納したArrayListを以下のように作成する。
import java.util.ArrayList;
import java.util.List;
public class StudentApp {
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
}
}
ソート
このオブジェクトの入ったリストをソートするにはどうすればよいかが今回のテーマだ。
単純ソートアルゴリズム
まずは、どんなものでもソートできるアルゴリズムでのソートをやってみよう。
ここでは単純ソートで点数を降順に並べてみる。
public class StudentApp {
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
//単純ソートアルゴリズム
for(int i=0;i<list.size()-1;i++) {
for(int j=i+1;j<list.size();j++) {
if(list.get(i).score < list.get(j).score){
Student temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
//確認
for(Student s : list) {
s.showStatus();
}
}
}
○結果
名前:Ringo,点数:88
名前:Paul,点数:72
名前:George,点数:62
名前:John,点数:37
このように、アルゴリズムを使えばどんなデータでも並び替えることができる。
このやり方はJavaに限らず色々なプログラミング言語でもできるので、1つくらいはアルゴリズムでのソート方法を覚えておくとよい。
ちなみに名前を辞書順に並べる場合は以下
//単純ソートアルゴリズム
for(int i=0;i<list.size()-1;i++) {
for(int j=i+1;j<list.size();j++) {
if(list.get(i).name.compareTo(list.get(j).name)>0){
Student temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
//確認
for(Student s : list) {
s.showStatus();
}
文字列を辞書順に並べる際には
文字列1.compareTo(文字列2)
cmpareTo()メソッドを使って比較する。これは文字列1が辞書順に並べたときに前に来るときに-1,後ろに来るときに1,同じの場合0を返す。
なので今回の場合はもし正の値を返す場合には入れ替えを行えばよい
ComparableとCollections.sortを使う
クラス自体に順番付けの概念を定義して、それをもとにソートするという方法がある。まずはStudentクラスを以下のように変更する。
public class Student implements Comparable<Student>{
String name;
int score;
Student(String name,int score){
this.name=name;
this.score=score;
}
void showStatus() {
System.out.printf("名前:%s,点数:%d%n", this.name,this.score);
}
@Override
//並びが正しいときに負の値を返すようにする
//今回は基準点のthis.scoreが比較するo.scoreより大きいときが正しい順序
public int compareTo(Student o) {
return -(this.score- o.score);
}
}
まず、Comparableインターフェイスを実装し、抽象メソッドcompareToをオーバライドする。この際、戻り値として正しい並び順のときに負の値を返すように設定することがポイントだ。今回は降順に並べたいので基準となるthis.scoreが大きいときに負の値が変えるように設定する。
クラスに並び順の定義さえしてしまえば、あとはColllections.sort()をするだけだ
以下のようにmainメソッドを変更する。
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
Collections.sort(list);
//確認
for(Student s : list) {
s.showStatus();
}
}
Collections.sort(リスト)を用いることでそのクラスで定義された自然順序で並び替えてくれる。ちなみに、 以下のようにすることで逆順に並べることができる。
Collections.sort(list,Collections.reverseOrder());
並び替え専門のクラスを用意する
先程は、Studentクラスに自然順序を定義したが、それはせずに並び替るための専門のクラスを用意する方法もある。まずは以下のようにStudentクラスをもとに戻す
public class Student{
String name;
int score;
Student(String name,int score){
this.name=name;
this.score=score;
}
void showStatus() {
System.out.printf("名前:%s,点数:%d%n", this.name,this.score);
}
}
○以下のようにComparatorインターフェイスを実装したクラスを準備する。
import java.util.Comparator;
public class StudentOrder implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return -(o1.score - o2.score);
}
}
Comparatorインターフェイスを実装し、抽象メソッドcampareをオーバーライドする。ここの書き方は先程と同じで、o1を基準と考え正しい関係のときに負の値が返るように定義してやる。
そして、mainメソッドには以下のように記述する
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
Collections.sort(list,new StudentOrder());
//確認
for(Student s : list) {
s.showStatus();
}
}
Collections.sortの第2引数に並び順を定義したComparatorインターフェイスを実装したクラスのインスタンスを渡すことで、並び替えを行うことができる。
匿名クラスを使っての実装
先程はStudentOrderという並び替えのためだけのクラスをわざわざ作成したが、匿名クラスを使うことでこの手間を省くことができる。
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
Collections.sort(list,new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return -(o1.score - o2.score);
}});
//確認
for(Student s : list) {
s.showStatus();
}
}
さらにComparatorは抽象メソッドが1つの関数型インターフェイスなのでラムダ式を使うと以下のようにかける
public static void main(String[] args) {
List<Student> list= new ArrayList<>();
list.add(new Student("John",37));
list.add(new Student("Paul",72));
list.add(new Student("George",62));
list.add(new Student("Ringo",88));
Collections.sort(list,(s1,s2)-> -(s1.score-s2.score));
//確認
for(Student s : list) {
s.showStatus();
}
}
最終的にここまでシンプルにオブジェクトの並び替えを実装することができた。
JavaSE8以降という制限はあるが、環境が許せば使っていくとよいだろう。
参考
ラムダ式
コメント