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() を後から呼ぶよりも、意図が明確で可読性が高いため推奨されます。
sortedBy と sortBy の違い
混同されやすいポイントなので、明確に整理します。
| 関数 | 対象 | 動作 |
|---|---|---|
sortedBy | Iterable | 新しい List を返す |
sortBy | MutableList | その場で並び替える(破壊的) |
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についてでした。
最後までお読みいただき、ありがとうございました。










