質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.22%

Java Comparator.naturalOrder()の自然な順序とは?

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,121

BitCoin

score 53

ストリームAPIについて

ソースコード1について

maxメソッドと引数についてですが、Comparator.naturalOrder()は自然な順序で
比較するとリファレンスにありました。
該当のソースコード1で試したところ「いあ」という文字列が返りましたが、この自然な順序というのはどいうことでしょう?
なぜローマ字より先に全角の日本語がかえるのでしょうか?
辞書順ソート?

ソースコード2について

どうやらmaxメソッドの引数に(d1,d2) -> d1.length() - d2.length()とすると作成したストリームから最大長の文字列を返すらしいのですが、なぜ引数を二つとり、長さを比較すると最大の長さの文字列が返るのでしょうか?

よろしくお願いします。

該当のソースコード1

List<String> data = Arrays.asList("aaa","bb","cccc","d","g","いあ","あい");

Optional<String> result1 = data.stream().max(Comparator.naturalOrder());
result1.ifPresent(System.out::println);

該当のソースコード2

Optional<String> result2 =data.stream().max((d1,d2) -> d1.length() - d2.length());
result2.ifPresent(System.out::println);

返り値

ソース1 いあ
ソース2  cccc
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

オブジェクトの順序付け

あるクラスのインスタンス同士に順序を付ける方法は2通りあります。

  1. そのクラスにComparableインタフェースを実装する
  2. Comparatorインタフェースのオブジェクトを作成する

1.の方法で順序を付けることを自然順序付けと呼びます。

目的

リストなど、要素の集合をソートするというのはよくある要求です。「ソート アルゴリズム」などで検索すれば様々な記事がヒットするはずです。そこで解説しているのはもっぱら数値のソートですが、どのアルゴリズムを見ても、基本的にやっていることは「2つの要素の大小を比較して、結果により入れ替える」ことで、異なるのは比較する要素の取り出し方や入れ替えのルール程度です。
そこで、一般のオブジェクト2つに対して、その2つの大小を比較することができれば、数値のソートと同じようにソートができることになります。この大小を決定するのがComparableやComparatorなのです。

比較のメソッド

Comparableインタフェースは引数を1つとるcompareToメソッドを持ち、次のように使います。

// Stringは辞書順に並ぶよう順序付けするようComparable#compareToメソッドを実装している
String a = "a";
String b = "b";
// 一方のインスタンスのメソッドの引数に、そのインスタンスと比較するインスタンスを渡す
int i = a.compareTo(b);


Comparatorインタフェースは引数を2つとるcompareメソッドを持ち、次のように使います。

// 文字列の長さの大小で比較するComparator(後ほど解説)
Comparator<String> comparator = (x, y) -> x.length() - y.length();
String a = "a";
String b = "bbb";
// Comparatorオブジェクトのメソッドの引数に比較する2つのインスタンスを渡す
int i = comparator.compare(a, b);


これらのメソッドはint型の値を返します。これらのメソッドは、どちらがより大きいかを、返り値の正負で判断できるように設計する必要があります。
今便宜上、xというインスタンスがyより小さい、つまり昇順に並べた際xがyより先に並ぶということをx < yと表現することにすると、

  • Comparableの場合
    x < y と x.compareTo(y) < 0 が同値
  • Comparatorの場合
    Comparatorオブジェクトをcとしたとき、
    x < y と c.compare(x, y) < 0 が同値

になるようにします。

本題 - 回答

まずいずれのソースコードにも共通することですが、
Stream#maxメソッドは引数にComparatorオブジェクトを渡し、そのComparatorに従って順序付けして、その最大の要素、つまり昇順に並べて最後に来る要素を返すものです。

ソースコード1において、自然順序付けはすでに書いた通り、そのクラスのComparableインタフェースで定義された順序付けをすることを意味します。したがって、次の2つのComparatorは同じ順序付けをします。

Comparator<String> c1 = Comparator.naturalOrder();
Comparator<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値同士を引き算で比較するとオーバーフローする恐れがあるので注意が必要
また次のように書くことも可能

Optional<String> result2 = data.stream().max(Comparator.comparingInt(String::length));


こうすることで、「文字列の長さで比較」ということを明示的にできる

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/26 16:12

    回答ありがとうございます!
    思ったより深いところまで考えなければならないのですね。頑張って理解します!

    キャンセル

0

ソースコードの1については、

なぜローマ字より先に全角の日本語がかえるのでしょうか?

.maxを取っていますので、返ってくる結果は「辞書順ソート」での最後のものになります。

ソースコード2については、「比較関数が正ならd1を後に、負ならd2を後に」というようにソートしていきますので、長さの順に並びます(これは「習うより慣れろ」の側面も強いです)。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/26 09:38

    回答ありがとうございます。
    少しわからなかったことがあるのですが
    ソース2についてですが、「比較関数が正ならd1を後に、負ならd2を後に」という意味ですが

    (d1,d2) -> d1.length() - d2.length()をmax()にわたすと
    はd1とd2にdata.stream()のなかの"aaa","bb","cccc","d","g","いあ","あい"を渡しすべての長さを比較し、その差が一番小さかったd1を返すという認識であっているでしょうか?

    いずれにせよmax()の中でどのような操作が行われているのかわからず・・・・
    こういう場合は理解することはお手上げなのでしょうか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる