KENスクールブログ | パソコン教室・パソコン講座なら個別指導のKENスクール

BLOGKENスクールブログ

  1. KENスクール TOP >
  2. KENスクールブログ > プログラム > Listに格納されたオブジェクトのソート

プログラム

Listに格納されたオブジェクトのソート

KENスクールの動画配信サービス「KEN×ONLINE」

ラッパークラスのオブジェクトを格納したリストであれば基本選択法を使って並べ替えを行うか、java.util.Collectionsクラスのstaticなsort()メソッドを使って簡単にソートすることが可能です。

APIでArrayListクラスのsort()メソッドを調べると、このように記述されています。

※オーバーロードされたsort()メソッドは後述します。

リストオブジェクトのみを引数にする場合は、その内容のオブジェクトがComparableインターフェースを実装している必要がある、という意味です

Comparableインターフェースを実装している既存のクラスで主なものは、ラッパークラスの他、Stringクラスなどがあります。これらはすべて昇順のソートになっています。

では、次のようなオブジェクトが格納されたリストをソートするにはどうすればいいのでしょうか。

1人分のテストの成績を収めておくJavaBeansです。(Student.java)

 

上記を実行するアプリケーションクラス (SortTest1.java)

 

 

【実行結果】

id   Name    Score

——————–

 1   Mark       87

 2   John       90

 3   Jaxon      87

 4   Alice       75

単純にCollectons.sort(list)とするわけにはいきません。何の順番で並べ替えるか?これを指定しなくてはソートできませんね。

これを実現するには2つの方法があります。(他のAPIを使用する方法もあるようですが、ここでは取り上げません。)

  1. Studentクラスでlang.Comparable<T>インターフェースを実装しcompareTo<T>()メソッドをオーバーライドする
  2. 並べ替え専用のutil.Comparator<T>インターフェースを実装したクラスを定義し、compare()メソッドをオーバーライドしたオブジェクトを利用する

1.Comparableインターフェースの利用

1の方法で得点(Score)の高い順(降順)に並べ替えてみます。

前述のように、Comparableインターフェースを実装したクラスであればよいので、今回の場合、StudentクラスにComparableインターフェースが実装されていればこのメソッドを利用することができる、ということになります。

Studentクラスは既にSerializableインターフェースを実装しているので、クラス宣言は「public class Student implements Serializable,Comparable<Student>」となります。

※比較したいオブジェクトは同じStudentクラスのオブジェクトなのでジェネリクスでStudentを指定します。

この場合Comparableインターフェースの抽象メソッドpublic int compareTo(<T> obj)メソッドです。

ジェネリクスのTはもちろんStudentです。

compareTo()メソッドではint型の戻り値を返すのですが、自オブジェクトのscoreの値と他のオブジェクトのscoreの値の差を返すようにしておけば、リストに存在するすべてのオブジェクトの値を比較できます。

昇順にソートするには、引き算してマイナスの値が返れば順序を入れ替え、そうでない場合は入れ替えない、というようにリスト内のすべてのオブジェクトをソートしてくれます。値の大小ではなく、「プラスかマイナスか」が重要になります。

今回は降順にソートしたいので、引き算の解の+と-を反転させておきます。

 

Studentクラスに次のメソッドを追加します。

 

 

 

これを活用するためにアプリケーションクラスSortTest1.javaでCollectionsクラスのstaticなメソッドであるsort()を呼び出します。

【実行結果】

id   Name    Score

——————–

 2   John       90

 1   Mark       87

 3   Jaxon      87

 4   Alice      75

一つのキーのみでソートする場合はこれでいいのですが、同じ点数の場合「名前の昇順で並べ替える」などのように第2キーがある場合はこのままではうまくいきません。

もう一度compareTo()メソッドの内容を振り返ると、「マイナスの値が出たときに順序を入れ替える(昇順)」というところに注目します。

数値の大小にかかわらず返す値は「マイナスの値」あるいは「プラスの値」であれば何でも構わないので、自オブジェクトのscoreの値と他のオブジェクトのscoreの値を比較して、他オブジェクトのほうが小さい場合は+1を、自オブジェクトのほうが小さい場合は-1を、そして、同じ場合は0を返すようにしておけば昇順にソートできます。

今回のように降順の場合はプラスとマイナスを逆にします。

 

compareTo()メソッド改造1

 

今回の第2キーは「名前の昇順」だったので、これを満たすように修正します。文字列の辞書順比較なので、String#compareTo()メソッドを使いました。この改造で先ほどと同じ動作になりますが、elseに注目してください。elseに制御が移るということはscoreが同じであったということになりますので、ここで第2キーの並びを考え、同じような比較やメソッドの呼び出しあるいは演算を行えばいいということになります。

 

compareTo()メソッド改造2

 

 

【実行結果】

id   Name    Score

——————–

同じ点数でも順序が入れ替わった

 2   John       90

 3   Jaxon      87 

 1   Mark       87

 4   Alice      75

2.Comparatorインターフェースの利用

Collectionsクラスのsort()メソッドはオーバーロードされていて、APIではこのようになっています。

第2引数にjava.util.Comparatorインターフェースを実装するクラス(並べ替えを実装するクラス)のインスタンスを指定することによりソートを実現します。

Comparableインターフェースの利用では、JavaBeansの方に定義する必要がありましたが、こちらのケースはComparatorインターフェースを実装した並べ替え専用のクラスを別途定義するので、JavaBeansを修正する必要がありません。

 

Studentクラスを元に戻した形でStudent2クラスを定義します。(Student2.java)

 

 

java.util.Comparatorインターフェースを実装したクラスを作り、抽象メソッド通常のJavaBeansの形です。

compare()を実装します。

内容はComparable#compareTo()とほぼ同じです。scoreフィールドが別のクラスになるのでどちらもgetScore()及びgetName()メソッドを使うところが違うだけです。

 

並べ替え用のクラスと実装するメソッド(MyComparator.java)

 

 

アプリケーションクラスのソートメソッドはCollections.sort(list,[MyComparatorクラスのオブジェクト])となります。

アプリケーションクラス(SortTest2.java)

 

 

MyComparatorクラスを別途定義せずに、匿名クラスで処理をするとこうなります。結果はComparableインターフェースの利用と同様になります。

アプリケーションクラス(SortTest2_1.java)※ソートの部分のみ抜粋

 

Comparatorインターフェースはcompare()メソッドのみが定義された関数型インターフェースなので、ラムダ式が使用可能です。これらの方法を実装すれば、ソートの内容、どのフィールドでソートするか、昇順・降順はどうするかなど、アプリケーションクラスを設計するときに自由に決めることができる、というメリットがあります。

※ソートの部分のみ抜粋

 

なお、Object型の配列の場合も同じように処理できますが、Collentions.sort()ではなくArrays.sort()となります。

この記事に関連する講座

Listに格納されたオブジェクトのソート

詳しくはこちら