Kotlinのsortedbyについて

採用はこちら

Kotlin の sortedBy は、コレクションを扱う際に非常に頻繁に使われるソート関数です。

一方で、「null の扱い」「複数条件ソート」「sortBy との違い」など、誤解されやすいポイントも多く存在します。

本記事では、仕様に忠実かつ実務で安全に使える理解を目標に、sortedBy を詳しく解説します。

目次

sortedBy とは何か

基本的な役割

sortedBy は、指定したキー(比較基準)を使って、コレクションを昇順で並び替える関数です。

val sorted = list.sortedBy { it.property }

特徴

  • 元のコレクションは 変更されない
  • 新しい List<T> を返す
  • キーは Comparable を実装している型である必要がある
  • 関数型スタイルで安全に使える

基本的な使用例

数値のソート

val numbers = listOf(5, 1, 10, 3)
val sorted = numbers.sortedBy { it }

println(sorted) // [1, 3, 5, 10]

{ it } は「要素そのものを比較キーにする」ことを意味します。

オブジェクトのプロパティでソート

data class User(val name: String, val age: Int)

val users = listOf(
    User("Taro", 30),
    User("Hanako", 20),
    User("Jiro", 25)
)

val sortedUsers = users.sortedBy { it.age }

結果は age の昇順になります。

null を含む場合の挙動(重要)

仕様上の正しい挙動

sortedBy は内部的に compareValues を使って比較を行います。

この比較ルールでは、

null は「小さい値」として扱われる

という仕様になっています。

data class Item(val name: String, val price: Int?)

val items = listOf(
    Item("A", 100),
    Item("B", null),
    Item("C", 50)
)

val sorted = items.sortedBy { it.price }

結果の並び順は概ね次のようになります。

[B(null), C(50), A(100)]

つまり、昇順では null は先頭側に来る点に注意が必要です。

null を末尾にしたい場合

業務要件では「値がないものは最後にしたい」ケースが多いため、次のような書き方がよく使われます。

items.sortedBy { it.price ?: Int.MAX_VALUE }

※ 値域と衝突する可能性がある場合は、sortedWith で null を明示的に比較する方が安全です。

sortedByDescending との違い

降順に並び替えたい場合は、専用の関数を使います。

val sorted = users.sortedByDescending { it.age }
  • sortedBy:昇順
  • sortedByDescending:降順

reversed() を後から呼ぶよりも、意図が明確で可読性が高いため推奨されます。

sortedBysortBy の違い

混同されやすいポイントなので、明確に整理します。

関数対象動作
sortedByIterable新しい List を返す
sortByMutableListその場で並び替える(破壊的)

sortBy の例

val list = mutableListOf(3, 1, 2)
list.sortBy { it }

println(list) // [1, 2, 3]

使い分けの指針

  • 安全性・可読性重視 → sortedBy
  • 大量データ・メモリ削減 → sortBy(慎重に)

複数条件でソートしたい場合

連続して sortedBy を呼ぶ場合の正しい理解

Kotlin/JVM のソートは 安定ソート(TimSort) です。

そのため、連続ソートには意味があります。

users
    .sortedBy { it.name } // 副キー
    .sortedBy { it.age }  // 主キー

この場合、

  • age が主キー
  • name が副キー

として機能します。

ただし、可読性・意図の明確さ・パフォーマンスの観点から、実務では次の方法が推奨されます。

推奨:sortedWith + compareBy

val sorted = users.sortedWith(
    compareBy<User> { it.age }.thenBy { it.name }
)

降順を混ぜることも可能です。

val sorted = users.sortedWith(
    compareByDescending<User> { it.age }
        .thenBy { it.name }
)

パフォーマンス上の注意点

計算量

  • 内部実装:TimSort
  • 計算量:O(n log n)

selector が重い場合

sortedBy { expensiveCalculation(it) } のように、キー計算が重い場合は 何度も評価される可能性があります。

その場合は、キーを事前計算する方法が有効です。

val sorted =
    list
        .map { it to expensiveCalculation(it) }
        .sortedBy { it.second }
        .map { it.first }

実務でよくあるパターン

users
    .filter { it.age >= 20 }
    .sortedBy { it.age }
    .map { it.name }
  • フィルタ → ソート → 変換
  • Kotlin らしい安全で読みやすい書き方

まとめ

  • sortedByキー指定による昇順ソート
  • 元のコレクションは変更されない
  • 昇順では null は先頭に来る
  • 複数条件は sortedWith(compareBy().thenBy()) が最適
  • 可読性・安全性に優れた標準的なソート手法

以上、Kotlinのsortedbyについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次