<<前ページ目次次ページ>>
6.比較する その2
〜java.util.Comparator〜
 オブジェクトの比較方法もいろいろあります。よく使う標準の比較方法は、クラスの中にcompareToで指定すればよかったですね。第5章の続編として、この章では他の比較方法を使いたい場合を解説します。
予防接種の並び方
 さあ、今日はいやーな予防接種の日です。普段背の順で並んでいる梅組の子供たちも、今日は出席番号順に並ばなければなりません。第5章で、クラスStudentに、背をもとにオブジェクトを比較するメソッドcompareToを作りましたが、出席番号をもとに比較するには、どうすればいいのでしょう?

 実はクラスStudentの中ではなく、Comparator(java.util.Comparator)を実装する別のクラスをわざわざ作って、その中のメソッドcompareで比較方法を定義してやります。次のようになります。
import java.util.Comparator;

public class StudentNoComp implements Comparator {
        public int compare(Object o1, Object o2) {
                return ((Student)o1).getNo() - ((Student)o2).getNo();
        }
}
 比較すべき二つのオブジェクトは、どちらも引数で受け取ります。出席番号はint型なので、単にその差を返しています。別クラスなので、privateのインスタンス変数noには直接アクセスできないので、メソッドgetNoで出席番号を求めています。
 このクラスは、Studentがあって、初めて意味のあるものになります。Studentの付属品のようなものです。メソッドのみのクラスでちょっと変な感じがしますね。

 では第5章で登場した梅組を、出席番号順で並べ替えてみましょう。
import java.util.Arrays;
public class Main2 {
        public static void main(String[] args) {
                Student[] ume = new Student[5];
                ume[0] = new Student(2, "木下 保美", 141.5);
                ume[1] = new Student(5, "湯水 敦", 145.0);
                ume[2] = new Student(1, "相田 徹", 152.5);
                ume[3] = new Student(4, "目加田 重三", 136.0);
                ume[4] = new Student(3, "橋 航", 145.0);

                Arrays.sort(ume, new StudentNoComp());
                for (int i = 0; i < ume.length; i++) {
                        ume[i].printStudent();
                }
        }
}
 太字の部分が増えている以外は前の章と変わりません。そう、並べ替えのときに、先ほど作ったクラスStudentNoCompのオブジェクトを、2番目の引数で渡してやるだけでいいのです。なんて簡単なんでしょう。次のように出力されます。

   1 相田 徹 152.5
   2 木下 保美 141.5
   3 橋 航 145.0
   4 目加田 重三 136.0
   5 湯水 敦 145.0


身長が同じならば...

 では「背の順で並ぶけれど、同じ身長の人は出席番号順に並ぶ」というちょっと高度な問題を考えましょう。
 湯水くんと橋くんは同じ身長ですが、出席番号は橋くんが3、湯水くんが5なので、橋くん、湯水くんの順で並ぶことになります。そんなときはArrays.sortが安定したソートであることを利用して、次のように2段階でソートすればどうでしょう。
import java.util.Arrays;
public class Main3 {
        public static void main(String[] args) {
                Student[] ume = new Student[5];
                ume[0] = new Student(2, "木下 保美", 141.5);
                ume[1] = new Student(5, "湯水 敦", 145.0);
                ume[2] = new Student(1, "相田 徹", 152.5);
                ume[3] = new Student(4, "目加田 重三", 136.0);
                ume[4] = new Student(3, "橋 航", 145.0);

                Arrays.sort(ume, new StudentNoComp());
                for (int i = 0; i < ume.length; i++) {
                        ume[i].printStudent();
                }
                System.out.println();
                
                Arrays.sort(ume);               
                for (int i = 0; i < ume.length; i++) {
                        ume[i].printStudent();
                }
        }
}
 出力結果は次のようになります。
   1 相田 徹 152.5
   2 木下 保美 141.5
   3 橋 航 145.0
   4 目加田 重三 136.0
   5 湯水 敦 145.0

   4 目加田 重三 136.0
   2 木下 保美 141.5
   3 橋 航 145.0
   5 湯水 敦 145.0
   1 相田 徹 152.5

 あらかじめ出席番号順でソートされているので、その上で背の順でソートすれば、安定したソートなので、背の同じ人は、出席番号順に並ぶことになります。


 上と同じことを、いっぺんのソートでやるにはどうしたらいいでしょうか?
 次のようなクラスを作り、メソッドcompareの中で、背が同じならば、出席番号で大小を決めるようにすればいいでしょう。
import java.util.Comparator;

public class StudentHeightNoComp implements Comparator {
	public int compare(Object o1, Object o2) {
		Student s1 = (Student) o1;
		Student s2 = (Student) o2;

		double h1 = s1.getHeight();
		double h2 = s2.getHeight();

		if (h1 == h2) {
			return s1.getNo() - s2.getNo();
		} else if (h1 > h2) {
			return 1;
		} else {
			return -1;
		}
	}
}
 次のように一度のソートですみます。
import java.util.Arrays;
public class Main4 {
        public static void main(String[] args) {
                Student[] ume = new Student[5];
                ume[0] = new Student(2, "木下 保美", 141.5);
                ume[1] = new Student(5, "湯水 敦", 145.0);
                ume[2] = new Student(1, "相田 徹", 152.5);
                ume[3] = new Student(4, "目加田 重三", 136.0);
                ume[4] = new Student(3, "橋 航", 145.0);

                Arrays.sort(ume, new StudentHeightNoComp());
                for (int i = 0; i < ume.length; i++) {
                        ume[i].printStudent();
                }       
        }
}

 ちなみにこの方がソート回数が少ない分、先ほどのやり方より処理時間が短くて済みます。Arrays.sort、便利なので使ってみてください。ただしくれぐれも、オブジェクトの大小を規定する、メソッドcompareToやcompareの中身は慎重に作ってください。



使い捨てクラス

 ところでStudentNoCompやStudentHeightNoCompは、Arrays.sortの引数として1回だけ使われるようです。いわば使い捨てのクラスです。それをわざわざ別クラスで作るのもめんどうくさい感じです。引数のnewのところに、直接中身を書けないか、これが匿名クラスです。
 使う場所で直接定義するわけですね。わかりにくいので図で別クラス方式と匿名クラス方式の対応を示します。




 別クラス方式のクラスStudentNoCompの赤く囲んだ部分が、newでコンストラクタを呼び出しているところに、すっぽり入ると、匿名クラス方式になります。
 なお青の下線部分のComparatorは、このクラスが実装するinterfaceの名前になります。つまり青い枠の中は、「名前はないけれど、Comparatorを実装するクラスで、メソッドcompareはこんな風に定義しますよ。」となるわけです。それをいきなりnewしてオブジェクトを生成しているわけです。
 どうですか?ちょっとかっこいいですか?でもへたをするとわかりにくくなり、バグの原因になるので要注意です。予防接種や身体検査、通知表をもらうとき、卒業証書授与と、出席番号順に並ぶことは結構ありそうなので、クラスStudentNoCompは作っておいてもよさそうですね。

次回は、梅組に転校生が来たときについて考察します。乞うご期待。

<<前ページ目次次ページ>>
Copyright (c) 2004-2005 Nagi Imai All Rights Reserved..