Java | Java 詳細・モダン文法:Stream API 深掘り – sorted

Java Java
スポンサーリンク

sorted を一言でいうと

Stream#sorted は、ストリームに流れてくる要素を「並べ替えた新しいストリーム」を返す中間操作です。
「順番を整える係」が sorted だと思ってください。


sorted の基本と「自然順序」

引数なし sorted の意味

sorted()(引数なし)は、要素の「自然順序(natural order)」で並べ替えます。
自然順序とは、Comparable を実装しているクラスが自分で定義している「標準の並び順」です。

例えば String なら辞書順、Integer なら数値の昇順が自然順序です。

import java.util.List;

public class SortedBasic {
    public static void main(String[] args) {
        List<String> names = List.of("Charlie", "Alice", "Bob");

        List<String> sorted =
                names.stream()
                     .sorted()   // 自然順序(String の辞書順)
                     .toList();

        System.out.println(sorted); // [Alice, Bob, Charlie]
    }
}
Java

このとき、元の names の順番は変わらず、新しいリストだけがソートされています。


Comparator を使った sorted の設計

引数あり sorted の基本形

「自然順序ではなく、自分で決めたルールで並べたい」ときは、Comparator を渡します。

<R> Stream<R> sorted(Comparator<? super R> comparator)
Java

例えば、文字列を「長さの昇順」で並べたい場合はこう書きます。

import java.util.Comparator;
import java.util.List;

public class SortedByLength {
    public static void main(String[] args) {
        List<String> names = List.of("Bob", "Alice", "Charlie");

        List<String> sortedByLength =
                names.stream()
                     .sorted(Comparator.comparingInt(String::length))
                     .toList();

        System.out.println(sortedByLength); // [Bob, Alice, Charlie]
    }
}
Java

Comparator.comparingInt(String::length) は「長さで比較する Comparator」を作るヘルパーです。


自作クラスと sorted

Comparable を実装して自然順序で並べる

自分のクラスを sorted()(引数なし)で並べたいなら、そのクラスに Comparable を実装して「標準の並び順」を決めておくのが王道です。

class User implements Comparable<User> {
    private final String name;
    private final int age;

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    String getName() { return name; }
    int getAge() { return age; }

    @Override
    public int compareTo(User other) {
        return this.name.compareTo(other.name); // 名前の辞書順
    }

    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}
Java

これで、次のように書けます。

import java.util.List;

public class SortedUserByName {
    public static void main(String[] args) {
        List<User> users = List.of(
                new User("Charlie", 25),
                new User("Alice", 20),
                new User("Bob", 30)
        );

        List<User> sorted =
                users.stream()
                     .sorted()   // User の compareTo に従ってソート
                     .toList();

        System.out.println(sorted); // [Alice(20), Bob(30), Charlie(25)]
    }
}
Java

compareTo の中身を「年齢順」に変えれば、自然順序も年齢順になります。

Comparator で柔軟に並べ替える

「自然順序は名前順にしておきたいけど、たまに年齢順でも並べたい」といった場合は、Comparator を渡す sorted を使います。

import java.util.Comparator;
import java.util.List;

public class SortedUserByAge {
    public static void main(String[] args) {
        List<User> users = List.of(
                new User("Charlie", 25),
                new User("Alice", 20),
                new User("Bob", 30)
        );

        List<User> sortedByAge =
                users.stream()
                     .sorted(Comparator.comparingInt(User::getAge))
                     .toList();

        System.out.println(sortedByAge); // [Alice(20), Charlie(25), Bob(30)]
    }
}
Java

Comparator.comparingInt(User::getAge) は「年齢で昇順に並べる Comparator」です。

さらに、「年齢昇順、同じ年齢なら名前順」のような複合条件も簡単に書けます。

Comparator<User> byAgeThenName =
        Comparator.comparingInt(User::getAge)
                  .thenComparing(User::getName);

List<User> sorted =
        users.stream()
             .sorted(byAgeThenName)
             .toList();
Java

sorted の位置づけとパイプライン設計

filter / map / distinct / sorted の流れ

Stream の中での sorted の役割は、「最終的な順番を整えること」です。
よくある流れは、次のような順番です。

絞り込みは filter
形を変えるのは map
重複を消すのは distinct
順番を整えるのが sorted

例えば、「20 歳以上のユーザーの“重複しない名前”を“名前順”に並べる」ならこうなります。

List<String> names =
        users.stream()
             .filter(u -> u.getAge() >= 20) // 絞り込み
             .map(User::getName)           // 変換
             .distinct()                   // 重複排除
             .sorted()                     // 名前の自然順(辞書順)
             .toList();
Java

左から右に読むだけで、「何をしているか」が自然に追えるはずです。


sorted のコストと注意点

全体を並べ替える=それなりに重い

sorted は、内部的には「全要素を一度集めてからソート」します。
つまり、要素数が多いほど計算量もメモリ使用量も増えます。

通常のサイズのリストなら気にしなくて構いませんが、「とにかく重い処理を避けたい場面」では、
本当にソートが必要か、ソートする要素数を減らせないか(先に filterlimit を入れるなど)を意識しておくとよいです。


まとめ:sorted を自分の言葉で整理する

sorted をあなたの言葉でまとめるなら、

「ストリームの要素を、自然順序または指定した Comparator に従って並べ替えた新しいストリームを返す中間操作」

です。

特に意識しておきたいのは、

引数なしは Comparable による自然順序
引数ありは Comparator で自由にルールを決められる
filter / map / distinct のあとに置くと、パイプラインの意図が読みやすい
自作クラスでは、自然順序を使うなら Comparable、柔軟に並べたいなら Comparator を設計する

というあたりです。

タイトルとURLをコピーしました