###オブジェクトの順序付け
あるクラスのインスタンス同士に順序を付ける方法は2通りあります。
0. そのクラスにComparableインタフェースを実装する
0. Comparatorインタフェースのオブジェクトを作成する
1.の方法で順序を付けることを自然順序付けと呼びます。
###目的
リストなど、要素の集合をソートするというのはよくある要求です。「ソート アルゴリズム」などで検索すれば様々な記事がヒットするはずです。そこで解説しているのはもっぱら数値のソートですが、どのアルゴリズムを見ても、基本的にやっていることは「2つの要素の大小を比較して、結果により入れ替える」ことで、異なるのは比較する要素の取り出し方や入れ替えのルール程度です。
そこで、一般のオブジェクト2つに対して、その2つの大小を比較することができれば、数値のソートと同じようにソートができることになります。この大小を決定するのがComparableやComparatorなのです。
###比較のメソッド
Comparableインタフェースは引数を1つとるcompareToメソッドを持ち、次のように使います。
java
1// Stringは辞書順に並ぶよう順序付けするようComparable#compareToメソッドを実装している
2String a = "a";
3String b = "b";
4// 一方のインスタンスのメソッドの引数に、そのインスタンスと比較するインスタンスを渡す
5int i = a.compareTo(b);
Comparatorインタフェースは引数を2つとるcompareメソッドを持ち、次のように使います。
java
1// 文字列の長さの大小で比較するComparator(後ほど解説)
2Comparator<String> comparator = (x, y) -> x.length() - y.length();
3String a = "a";
4String b = "bbb";
5// Comparatorオブジェクトのメソッドの引数に比較する2つのインスタンスを渡す
6int i = comparator.compare(a, b);
これらのメソッドはint型の値を返します。これらのメソッドは、どちらがより大きいかを、返り値の正負で判断できるように設計する必要があります。
今便宜上、xというインスタンスがyより小さい、つまり昇順に並べた際xがyより先に並ぶということをx < yと表現することにすると、
x < y と x.compareTo(y) < 0 が同値
Comparatorオブジェクトをcとしたとき、
x < y と c.compare(x, y) < 0 が同値
になるようにします。
###本題 - 回答
まずいずれのソースコードにも共通することですが、
Stream#maxメソッドは引数にComparatorオブジェクトを渡し、そのComparatorに従って順序付けして、その最大の要素、つまり昇順に並べて最後に来る要素を返すものです。
ソースコード1において、自然順序付けはすでに書いた通り、そのクラスのComparableインタフェースで定義された順序付けをすることを意味します。したがって、次の2つのComparatorは同じ順序付けをします。
java
1Comparator<String> c1 = Comparator.naturalOrder();
2Comparator<String> c2 = (s1, s2) -> s1.compareTo(s2);
ソースコード2においては、Comparator#compareメソッドの実装をラムダ式で行っています。
これで文字列の短い順に並ぶ理由についてですが、少し数学の話をします。
任意の実数a,bに対して、a < b と a - b < 0 は同値です。
今このmaxメソッドに渡しているComparatorは、2つの文字列のlength()、つまり長さの差を取ることで、長さの大小を比較していることになります。この符号が正か負かで、どちらが長いかが決定できます。
そして負であるとき、つまりd1のほうが短いとき、メソッドの設計の話からd1が昇順で先に並ぶことになります。
maxメソッド内ではこれを自動でやってくれるのでd1とかd2に当たるものが何か外部からは見えませんが、「短いものから先に並ぶ」という要件にはなっています。
※今回の場合は「文字列の長さ」のため、両方0以上の値であるから引き算で比較できたが、一般のint値同士を引き算で比較するとオーバーフローする恐れがあるので注意が必要
また次のように書くことも可能
java
1Optional<String> result2 = data.stream().max(Comparator.comparingInt(String::length));
こうすることで、「文字列の長さで比較」ということを明示的にできる
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
2017/08/26 07:12