LoginSignup
6
8

More than 3 years have passed since last update.

(JavaSE8Gold試験対策)ComparableやComparatorと仲良くなりたい人のメモ。

Last updated at Posted at 2020-07-21

間違いを見つけた方がいらっしゃったら優しく指摘してくださると嬉しいです。

はじめに

私自身、JavaGold試験勉強中に全然頭の整理ができなくて非常に混乱しました。そのため、振り返りを兼ねて整理しました。これで少しだけ仲良くなれた気がする٩( 'ω' )و

そもそも何なのよ?

ComparableやComparatorって何?と言うところから入りますが、共に並び替えや最小最大値取得のメソッドにおいて、要素の相互比較をするために用いられるインターフェースです。まぁ、それは分かるよ...って感じですよね。

「自然順序付け」って聞いてピンときますか?

何となく昇順なのかな〜〜。くらいの理解で、私はピンときませんでした。
ただ、これが何なのかピンと来るだけで、理解へのハードルが格段に下がることになります。
そのため、まずはコチラの説明から入らせてください。

自然順序付けはComparableに従った並び替えのこと

JavaのAPIドキュメントを参照していると、この「自然順序付け」が頻繁に出てきます。
ただ、見ていくと分かるのですが、もはや「自然順序付け」と「コンパレータ」はセット商品みたいに紹介されてます。

スクリーンショット 2020-07-20 20.57.47.png
※Java APIドキュメント(Collectionsクラス)より抜粋

ここで何となくピンと来る方も居ると思うんですが、自然順序付けとはComparableで実装された順序に並び替える事を示しています。
下記にComparableのJava APIドキュメントを抜粋します。

public interface Comparable
このインタフェースを実装する各クラスのオブジェクトに全体順序付けを強制します。この順序付けはクラスの自然順序付けと呼ばれ、このクラスのcompareToメソッドは自然比較メソッドと呼ばれます。
Java APIドキュメント-Comparable

それではComparableとは何なのでしょうか。
位置付けとしてはクラス(オブジェクト)の共通メソッドのようです。
共通メソッドと言えばequalsやtoStringなどがパッと思い浮かんだのではないでしょうか?
ただし、他と違ってComparableはObjectには宣言されていないません。そのため、必要に応じてクラス作成時に実装する必要があります。
なお、Comparableを実装せずに自然順序付けに従ってソート実行しようとすると"ClassCastException"が発生するので、対象のクラスがComparableを実装しているか注意しておきましょう。

ちなみに、昇順でも降順でも実装可能なのですが、自然順序付けで並び替えるメソッドに明確に”昇順”と記載されているもの(Collectionsクラスのsortなど)がありますので、自然順序付けは、おそらく昇順実装が基本なのかと思います。

それじゃあ、Comparatorは??

Comparatorは、Comparableよりも後から出てきたインターフェースです。
主な利用用途はおそらく以下の感じだろうと推測。

・Comparableが実装されていないクラスまたはフィールドの並び替えを行いたい
・ちょっと特殊な並び替えを行いたい

そして、これらの期待に応えるためなのか、Comparatorは柔軟に実装可能な「関数型インターフェース」となっています。また、より実装を簡素化可能なstaticメソッドも提供されており、Comparableを実装するために、Comparatorのstaticメソッド使っちゃうとかもあるみたいです(笑)

また、Comparatorがnullの場合は、メソッドによってComparableが適用されたり、"NullPointerException"となったりするようです。

で?Comparableなのか?Comparatorなのか?

試験問題でコイツらと遭遇したら、どう判断したら良いのでしょうか?
試験対策としてだけ考えるのであれば、極論は以下の通りに覚えておけば問題ないと思います。一部Comparableを許容していないメソッドもありましたが、基本はセットものとして覚えてOKかと。

スクリーンショット 2020-07-20 21.29.00.png

その上で、それぞれの実装の違い(メソッド名や引数の数の違い)をしっかり覚えればヨシ!

実装の違いについて

それぞれの実装の違いをまとめると以下のような感じになります。
オーバーライドすべきメソッドと引数の数は試験に必出なので覚えておきましょう。

パッケージ インタフェース 抽象メソッド 引数の数 戻り値の型 説明
java.lang Comparable compareTo 1 int 自身と引数オブジェクトを比較。自然順序付けに使用
java.util Comparator compare 2 int 引数同士を比較。任意の順序付けを行う

次項から具体的な実装例を紹介していこうと思います。

実装方法

それぞれの実装例を紹介いたします。
また、もし可能であれば是非実機で確認してみるのをオススメします。

↓↓紹介するサンプルを以下にコピペでサクッと動くはず
Javaクラウド実行環境:https://paiza.io/

Comparable

先に説明したとおり、該当クラスに対してComparableの実装を行います。
実装例は以下のとおりです。

①クラスに実装


class Book implements Comparable<Book> {     // 実装宣言。<>内は比較対象です。

    public Integer id;
    public String title;

    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    @Override
    public int comareTo(Book book){          // Comparableの抽象メソッド
        return this.id.compareTo(book.id);   // 自分自身と引数を比べる
    }
}

compareToメソッドでは、指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数を返します。
因み(?)にcompareToメソッドの中で、さらにcompareToて何なん?と思うのは私だけでしょうか。

それではComparableを使って並び替えをしてみましょう。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // ソート対象の作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // ソート実行
        bookList.sort();  // 引数指定してないのでComparableに従って自然順序づけされます

        // ソート対象を表示する
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記

これ見てるとジェネリックがあるからこその実装なんだろうなぁ〜と、ぼんやり思います。

そして、Comparableに興味が出てきたあなた。
JavaのAPIドキュメントでもっと詳しく(正確に)知ってください٩( 'ω' )و ←投げた

Java APIドキュメント - Comparableインターフェース

Comparator

それでは続いてComparatorにいきましょう。
こちらは「関数型インターフェース」なので様々な実装方法が取れます。
私が分かる範囲にはなりますが、それぞれサンプルを紹介しますね〜。

①クラスに実装

これはComparableとほぼ一緒な感じです。
Comparatorをこの形で使用するのは、あんまり意味ない気がします。個人的に。。

※後続の実装方法でもこのBookクラスを使用する前提にしたいので、紐付けできるようにコメントにどの実装方法に関連するコードかを番号付けしておきます。


class Book implements Comparator<Book> {         //①実装宣言。<>内は比較対象です。

    public Integer id;
    public String title;

    Book(){}                                     //①これがないとnewした時に怒られます

    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    public int getId(){                          // ②Comparator.comparingの引数に使う
        return this.id;
    }

    public String getTitle(){                    // ②Comparator.thenComparingの引数に使う
        return this.title;
    }

    @Override 
    public int compare(Book book1, Book book2){   // ①Comparatorインタフェースの抽象メソッド
        return book1.id - book2.id;              // idの昇順で並び替え
    }
}

compareメソッドでは、順序付けのために2つの引数を比較します。最初の引数が2番目の引数より小さい場合は負の整数、両方が等しい場合は0、最初の引数が2番目の引数より大きい場合は正の整数を返します。
また、1つ目の引数-2つ目の引数で昇順、逆にすることで降順になるみたいです。

それでは実装したComparatorで並び替えをしてみましょう。
私は初見で衝撃(?)を受けたのですが、クラスに実装の場合、Comparatorは「Comparatorを実装したクラスをnew」して引き渡します。
あー...並べ替え対象の型と相違する型のComparator渡したら怒られそうですね。

import java.util.*;
public class Main {
    public static void main(String[] args) throws Exception {

        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        bookList.sort(new Book());  // ← 【注】Comparatorの渡し方が特殊なので必ず覚えて!Comparatorを含んだクラスのインスタンスを引き渡します!!!
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記

②ラムダ式(又は匿名クラス)で実装

関数型インターフェースであることを遺憾無く発揮できる書き方です。
ラムダ式で書けると言うことは、匿名クラスでも書けるので両方紹介します。

ラムダ式の実装サンプル

    Comparator<Book> comparator = 
        (b1, b2) -> b1.id.compareTo(b2.id);        // idの昇順

匿名クラスの実装サンプル

    Comparator<Book> comparator = new Comparator<Book>(){
        @Override
        public int compare(Book book1, Book book2) {
            return book1.id.compareTo(book2.id);   // idの昇順
        }
    };

※ComparableよりComparatorで実装すると多少遅くなるようです。
実装時はJavaのstaticインポート(static import)機構の使用も検討した方が良いらしいです。

それでは並び替えを実行してみましょう。
こちらは変数化せずに、直接Comparatorを実装しています。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        //Comparator(匿名クラス)を使ったソート実行
        bookList.sort(new Comparator<Book>(){
            @Override
            public int compare(Book book1, Book book2) {
                return book1.id.compareTo(book2.id);         // idの昇順
            }
        });

        //ソート結果を表示
        System.out.println("匿名クラス(idの昇順)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));

        //Comparator(ラムダ式)を使ったソート実行 ※わかりやすい用に降順に並べる
        bookList.sort((b1, b2) -> b2.id.compareTo(b1.id));  // idの降順

        //ソート結果を表示
        System.out.println("ラムダ式(idの降順)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
匿名クラス(idの昇順)
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記
ラムダ式(idの降順)
3:ハムスター飼育日記
2:ハウルの動く城
1:ハリーポッター

③Comparatorのstaticメソッドで実装

Comparatorでは便利なstaticメソッドが提供されています。
このstaticメソッドを組み合わせることで並び替え対象のメンバ優先順位付けをすることも出来ます。
単純な並び替えであれば、コイツを使うのが一番楽チンだと思います。

StreamAPIを使用したサンプルは以下のとおり。

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリーポッター"));

        //ストリーム化する
        Stream<Book> streamList = bookList.stream();

        //Comparator.comparingを使用して並び替えて表示 
        streamList.sorted(Comparator.comparing(Book::getId)  // idを第一優先で並び替え
                .thenComparing(Book::getTitle))              // titleを第二優先で並び替え
                .forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
2:ハムスター飼育日記

comparing()とthenComparing()はチェーンさせて使用します。
もちろん、comparing()だけでも利用可能ですし、thenComparing()を更にチェーンさせることも可能です。
また、訂正するとstaticと言っておきながら、thenComparing()はdefaltメソッドです^^;
他にもComparatorでは以下のようなメソッドが提供されています。

# メソッド名 説明
comparing ソート・キーを抽出する関数を受け取り、そのソート・キーで比較するコンパレータを返す
thenComparing 辞書式順序コンパレータをもう一方のコンパレータとともに返す
naturalOrder 自然な順序でComparableオブジェクトを比較するコンパレータを返す
(defaltメソッド)
nullsFirst nullをnull以外より小さいとみなす、nullフレンドリのコンパレータを返す
nullsFirst nullをnull以外より大きいとみなす、nullフレンドリのコンパレータを返す
reversed このコンパレータの逆順を義務付けるコンパレータを返す
(defaltメソッド)
reverseOrder 自然順序付けの逆を義務付けるコンパレータを返す

そして、興味を持ったら是非Java APIドキュメントをご参照ください。
私の説明は雑ですので(笑)
Java APIドキュメント - Comparatorインターフェース

④実はComparatorでなくても良い件

ここまでComparatorを使用した例をみてきましたが、Comparatorと同じ作りであれば、実は他のメソッドでも問題なく利用できちゃうぽいです。
何やねんって感じですが、恐らくComparatorのstaticメソッドを使うのと同じノリなんだと思います。
(コレでコンパレータを返却出来ているということだと)

それでは実際に例を見てみましょう。
まず、Compartorの代わりになるなんちゃってクラスを作成します。

class LengthChecker {
    public static int check(Book b1, Book b2){         // Comparator実装時にオーバーライドするcompareメソッドと違いstaticメソッドにできる
        return b1.title.length() - b2.title.length();  // title文字数の昇順
    }
}

作成したらComparatorの代わりにメソッドに引数として渡してみます。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // ソートを実行する
        bookList.sort(LengthChecker::check);  // ← Comparatorと違いメソッドまで指定が必要

        // ソート結果を表示する
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
2:ハウルの動く城
1:ハリーポッター
2:ハムスター飼育日記

上手くいってまうんやぁ...。
基本的に引数でComparator指定となっている所は置き換えられるぽいです。
この辺の仕様について調べきれていませんが、問題として出ていたので「へ〜」くらいには覚えておくのが良いのかもしれません。

主な利用クラスやインターフェースのご紹介

ComparableやComparatorを使用して並び替えや大小要素取得などを行なっているメソッドを紹介します。超簡単に書いているので詳細はJavaのAPIドキュメントを参照ください。
と言うより、この期にJavaのAPIドキュメントに慣れておくのを推奨します!!!

Collection/Mapインターフェース

このインターフェースを実装したクラスやdefaultやstaticメソッドで非常によく使われています。
なお、前項で出てきたBookクラスが実装されている想定で読んでくさい〜。

①Listインターフェース(defaltメソッド)

デフォルトメソッドとして並び替えのメソッドが用意されています。

# メソッド名 Comparable Comparator 説明
sort ソート

実装サンプル

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象のリストを作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // Comparatorにソート
        bookList.sort(new Book());

        // ソート結果を表示する
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));

    }
}
実行結果
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記

Listインターフェースの詳細はコチラ

②Collectionsクラス(staticメソッド)

Collectionsインターフェースの実装クラスで、定義されているのは全てstaticメソッドです。

# メソッド名 Comparable Comparator 説明
sort ソート
reverseOrder sortの引数に指定。逆順にソートするコンパレータを返却
binarySearch バイナリ・サーチ・アルゴリズムを使用して、指定されたリストから指定されたオブジェクトを検索す。バイナリ・サーチは二分探索のこと。実行前に指定の順序付けで昇順にソートされている必要有
min ソート後に指定されたコレクションの最小の要素を返却
max ソート後に指定されたコレクションの最大の要素を返却

実装サンプル

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //Bookを生成
        Book book1 = new Book(3, "とっとこハム次朗");
        Book book2 = new Book(1, "ハムスター飼育日記");
        Book book3 = new Book(2, "ハム助ごころ。");

        //ソート用にリストを生成        
        List<Book> bookList = new ArrayList<>();
        bookList.add(book1);
        bookList.add(book2);
        bookList.add(book3);

        // Comparableにソート(Bookのidをソートのキーに使用している前提)
        System.out.println("Comparable");
        System.out.println("-----①-----");  
        Collections.sort(bookList);                                       //idの昇順に並び替え
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));  
        System.out.println("-----②-----");        
        int result = Collections.binarySearch(bookList, book3);           // bookList中のbook3の"Index番号"を返す
        System.out.println(result);
        System.out.println("-----③-----");        
        Collections.sort(bookList, Collections.reverseOrder());           //Comparable compareToの逆順に並び替える
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));  
        System.out.println("-----④-----");
        Book minBook = Collections.min(bookList);                         //idが最小のBookを返す
        System.out.println(minBook.id + ":" + minBook.title);
        System.out.println("-----⑤-----");
        Book maxBook = Collections.max(bookList);                         //idが最大のBookを返す
        System.out.println(maxBook.id + ":" + maxBook.title);

        // Comparatorにソート(Bookのidをソートのキーに使用している前提)
        System.out.println("Comparator");
        System.out.println("-----①-----");  
        Collections.sort(bookList, new Book());                           //idの昇順に並び替え
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));  
        System.out.println("-----②-----"); 
        result = Collections.binarySearch(bookList, book2);               // bookList中のbook2の"Index番号"を返す
        System.out.println(result);
        System.out.println("-----③-----");                    
        Collections.sort(bookList, Collections.reverseOrder(new Book())); //Comparator compareの逆順に並び替える
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));  
        System.out.println("-----④-----");       
        minBook = Collections.min(bookList, new Book());                  //idが最小のBookを返す
        System.out.println(minBook.id + ":" + minBook.title);
        System.out.println("-----⑤-----");               
        maxBook = Collections.max(bookList, new Book());                  //idが最大のBookを返す
        System.out.println(maxBook.id + ":" + maxBook.title);

    }
}
実行結果
Comparable
----------
1:ハムスター飼育日記
2:ハム助ごころ
3:とっとこハム次朗
----------
1
----------
3:とっとこハム次朗
2:ハム助ごころ
1:ハムスター飼育日記
----------
1:ハムスター飼育日記
----------
3:とっとこハム次朗
Comparator
----------
1:ハムスター飼育日記
2:ハム助ごころ
3:とっとこハム次朗
----------
0
----------
3:とっとこハム次朗
2:ハム助ごころ
1:ハムスター飼育日記
----------
1:ハムスター飼育日記
----------
3:とっとこハム次朗

Collectionsクラスの詳細はコチラ

③Arraysクラス

配列を操作するための様々なメソッドが提供されています。
紹介するメソッドは全てstaticメソッドです。

# メソッド名 Comparable Comparator 説明
parallelSort 指定範囲内でソート
sort ソート
binarySearch バイナリ・サーチ・アルゴリズムを使用して、指定されたリストから指定されたオブジェクトを検索す。バイナリ・サーチは二分探索のこと。実行前に指定の順序付けで昇順にソートされている必要有。範囲指定することも可能。

実装サンプル
型ごとに、めちゃくちゃ一杯種類がありますが端折ります。
詳細はJavaのAPIドキュメントをご参照くださいませ。

sample.java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // ソート対象の配列を作成
        String[] array = {"ハムスケ","ハムコ","ハムジロウ","ハムコ"};

        // Comparableにソート(StringのComparableが利用される)
        System.out.println("Comparable");
        System.out.println("-----①-----");
        Arrays.parallelSort(array, 0, 2);     // 指定範囲内のみ昇順に並び替え
        for (String s: array) System.out.print(s + " ");
        System.out.println();
        System.out.println("-----②-----");
        Arrays.sort(array);                   // 昇順に並び替え
        for (String s: array) System.out.print(s + " ");
        System.out.println();
        System.out.println("-----③-----");
        int result = Arrays.binarySearch(array, "ハムコ");  // 指定したkeyのIndex番号を返す(同一オブジェクトが複数ある場合、どれがヒットするかは保証しない
        System.out.println(result);

        // ソート対象の配列を作成
        String[] array2 = {"ハムスケ","ハムコ","ハムジロウ","ハムコ"};

        // Comparatorを作成
        Comparator<String> comparator = new Comparator<String>(){
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        };

        // Comparatorにソート
        System.out.println("Comparator");
        System.out.println("-----①-----");
        Arrays.parallelSort(array2, 2, 4, comparator);       // 指定範囲内のみ昇順に並び替え
        for (String s: array2) System.out.print(s + " ");
        System.out.println();
        System.out.println("-----②-----");
        Arrays.sort(array2, comparator);                     // 昇順に並び替え
        for (String s: array2) System.out.print(s + " "); 
        System.out.println();
        System.out.println("-----③-----");  
        result = Arrays.binarySearch(array2, "ハムジロウ", comparator);  // 指定したkeyのIndex番号を返す
        System.out.println(result);
    }
}
実行結果
Comparable
----------
ハムコ ハムスケ ハムジロウ ハムコ 
----------
ハムコ ハムコ ハムジロウ ハムスケ 
----------
1    //おそらく0か1が表示されると思われ...
Comparator
----------
ハムスケ ハムコ ハムコ ハムジロウ 
----------
ハムコ ハムコ ハムスケ ハムジロウ 
----------
3

binarySearchがややこしいですが、下記両方を満たす場合は結果がマイナス(=定義されない)となるみたいです。
・binarySearch実行前にsortが実行されていない
・渡されたcomparatorが昇順ソートではない
なお、Comparableは昇順・降順に関わりなく検索結果が返りますが、そもそも昇順実装前提なのだと思います。二分探索の何たるかをよく分からずに書いていますので、詳細な挙動はAPIドキュメントを参照してみてください。

Arraysクラスの詳細はコチラ

④TreeSet/TreeMapクラス

標準で並び替えサポートされたコレクションであるため、コンストラクタ呼び出し時に並び替えが実行されます。その後も、要素の追加のたびにComparableやComparatorが作動します。

【ポイント】
自然順序付けに基づく場合、Comparableの影を全く感じませんが、該当クラスにComparableが実装されていないと例外が発生することに注意です!!!

実装サンプル
同じような感じかなと思うのでTreeSetのみサンプル掲載。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // Comparatorを作成
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  // タイトル文字数の長い順
            }
        };

        // Comparableに作成
        TreeSet<Book> book = new TreeSet<>();
        book.add(new Book(2, "とっとこハム二郎"));
        book.add(new Book(3, "ハム助の知らない世界"));
        book.add(new Book(1, "月曜からハムまつり"));
        System.out.println("<Comparable>");
        book.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));

        // Comparatorに作成
        TreeSet<Book> book2 = new TreeSet<>(comparator);
        book2.add(new Book(2, "とっとこハム二郎"));
        book2.add(new Book(3, "ハム助の知らない世界"));
        book2.add(new Book(1, "月曜からハムまつり"));
        System.out.println("<Comparator>");
        book2.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));
    }
}
実行結果
<Comparable>
1:月曜からハムまつり
2:とっとこハム二郎
3:ハム助の知らない世界
<Comparator>
2:とっとこハム二郎
1:月曜からハムまつり
3:ハム助の知らない世界

TreeSetの詳細はコチラ/TreeMapの詳細はコチラ

StreamAPIインターフェース

ストリーム化したオブジェクトに対して並び替えなどの操作を行うことが可能です。
この中で前出のCollectionやMapインターフェース軍も使うことも可能です。

# メソッド名 Comparable Comparator 説明
sorted ソート
min ソートした結果の最小要素をOptional型で返却
max ソートした結果の最大要素をOptional型で返却

実装サンプル

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // Comparatorを作成
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  // タイトル文字数の長い順
            }
        };

        // Comparableにソート
        System.out.println("<Comparable:タイトルの短い順>");
        bookList.stream().sorted().forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));

        // Comparatorにソート
        System.out.println("<Comparator:タイトルの短い順>");
        bookList.stream().sorted(comparator).forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));  // ①sortedメソッドでソート
        Book minBook = bookList.stream().min(comparator).get();         // ②minメソッドで最小要素を取得
        System.out.println("<一番タイトルの短いBook>");
        System.out.println(minBook.getId() + ":" + minBook.getTitle());
        Book maxBook = bookList.stream().max(comparator).get();         // ③maxメソッドで最大要素を取得
        System.out.println("<一番タイトルの長いBook>");
        System.out.println(maxBook.getId() + ":" + maxBook.getTitle()); 
    }
}
<実行結果>
Comparable:タイトルの短い順
2:ハムスター飼育日記
2:ハウルの動く城
1:ハリーポッター
Comparator:タイトルの短い順
2:ハウルの動く城
1:ハリーポッター
2:ハムスター飼育日記
一番タイトルの短いBook
2:ハウルの動く城
一番タイトルの長いBook
2:ハムスター飼育日記

StreamAPIの詳細はコチラ

最後に振り返りクイズ

JavaSE8Goldぽく問題出してみます。
まず、以下のコードがあります。


  List<Book> bookList = new ArrayList<>();
  bookList.add(new Book(3, "ハリー・ポッター"));
  bookList.add(new Book(1, "ハウルの動く城"));
  bookList.add(new Book(2, "ハムスター飼育日記"));

  Collections.sort(bookList); // ← ココ。

このプログラムを実行させて、Book.idの昇順に並び替えるためにBookクラスに実装すべきコードはどれでしょう〜。


// ①
    public int compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ②
    public boolean compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ③
    public int compare(Book book1, Book book2){
        return book1.id - book2.id;
    }

// ④
    public boolean compare(Book book1, Book book2){
        return book1.id > book2.id;
    }




正解は①です!
引数にComparatorを渡していないので、自然順序付け(Comparable)によるソートですね。
Comparableの抽象メソッドはcompareToです。
また、引数は1つで、返り値はint型です。

まとめ

試験が終わった後だと、ゆっくり振り返えることができますが、試験前は黒本周回とインプットに忙しくて中々に余裕がないですよね^^;
...試験前にやってたら、もっと点数取れた気がしてならない(TvT)

参考文献

Java SE8 APIドキュメント達を参考にしました

Comparableインターフェース
Comparatorインターフェース
Collectionsクラス
Listインターフェース
TreeMapクラス
TreeSetクラス
Arraysクラス
StreamAPIクラス

また、試験勉強として紫本と黒本も読んでいたのでソチラの知識も融合しています。

6
8
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8