Edit Page

並べ替え

要素の順番は、ある種のコレクションには重要な側面です。 例えば、同じ要素を持つ2つのリストでも、要素の順番が異なればこれらのリストは等しいとはみなされません。

Kotlinでは、オブジェクトの順番を定義する方法はいくつかあります。

最初の方法としては、自然(natural)な順番があります。 これはComparableインターフェースの実装により定義される順番です。 自然な順番はその他の順番が指定されていない場合のソートで使われる順番です。

多くのビルトインの型は比較可能(comparable)です:

  • 数値型は伝統的な数値の順番を使います:10よりも大きく、-3.4f-5fよりも大きい、などです。
  • CharString辞書式順序を使います:baより大きく、worldhelloより大きいです。

ユーザー定義型に自然な順番を定義するためには、その型でComparableインターフェースを実装する必要があります。 これはcompareTo()関数を実装する事を意味します。 compareTo()は同じ型の別のオブジェクトを引数に取り、 どちらのオブジェクトが大きいかを示すIntの値を返します:

  • 正の値はレシーバオブジェクトの方が大きい事を示します。
  • 負の値はレシーバオブジェクトの方が小さい事を示します。
  • 0はオブジェクトが等しい事を示します。

以下はバージョンクラスの順番を、メジャーとマイナーのバージョン番号から決める例です。

class Version(val major: Int, val minor: Int): Comparable<Version> { override fun compareTo(other: Version): Int = when { this.major != other.major -> this.major compareTo other.major // compareTo()の中置形式 this.minor != other.minor -> this.minor compareTo other.minor else -> 0 } } fun main() { println(Version(1, 2) > Version(1, 3)) println(Version(2, 0) > Version(1, 5)) }

カスタムな順番は、どんな型のインスタンスでもお好みなようにソートする事を可能にしてくれます。 とりわけ、Comparableでは無いオブジェクトの順番を定義したり、Comparableな型の自然な順番以外の順番を定義したり出来ます。 ある型のカスタムな順番を定義するためには、Comparatorを作ります。 Comparatorcompare()関数を持ちます:これは、指定されたクラスの2つのインスタンスを引数に取り、それらの比較結果を数値として返します。 結果の数値は先に述べたcompareTo()と同様に解釈されます。

fun main() { //sampleStart val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length } println(listOf("aaa", "bb", "c").sortedWith(lengthComparator)) //sampleEnd }

lengthComparatorを使えば、辞書順では無く長さで文字列を並べる事が出来ます。

Comparatorを定義するもっと短い方法としては、標準ライブラリのcompareBy()関数を使う方法があります。 compareBy()は、インスタンスからComperableな値を返すラムダ関数を引数に取り、 その生成された値の自然な順番をカスタムな順番とします。

compareBy()を使えば、上記のlengthComparatorの例は以下のように書く事が出来ます:

fun main() { //sampleStart println(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length })) //sampleEnd }

Kotlinのコレクションpackageは、自然な順番、カスタムな順番、ランダムな順番などに並べ替える関数を提供しています。 このページでは、読み取り専用のコレクションをソートする関数を説明します。 これらの関数は指定された順番に並べ替えたコレクションを新しく生成して返します。 ミュータブルをインプレイスにソートする関数を知りたければ、 List特有のオペレーションを参照してください。

自然な順番

(訳注:Natural order)

基本となる関数、sorted()sortedDescending()は、 コレクションの要素をその自然の順番で昇順、または降順に並べ替えたコレクションを生成して返します。 これらの関数はComparableな要素のコレクションに対して使う事が出来ます。

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") println("Sorted ascending: ${numbers.sorted()}") println("Sorted descending: ${numbers.sortedDescending()}") //sampleEnd }

カスタムな順番

カスタムな順番でソートしたり、そもそもComperableで無いオブジェクトをソートしたい場合には、 sortedBy()sortedByDescending()関数があります。 これらは、コレクションの要素をComperableな値にマップするセレクタ関数を引数に取り、その値の自然な順番でコレクションをソートします。

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") val sortedNumbers = numbers.sortedBy { it.length } println("長さの昇順でソート: $sortedNumbers") val sortedByLast = numbers.sortedByDescending { it.last() } println("最後の文字で降順にソート: $sortedByLast") //sampleEnd }

コレクションのソートに使う為にカスタムな順番を定義するために、独自のComparatorを渡す事も出来ます。 そのためには、sortedWith()関数を使い、 独自に定義したComparatorをこれに渡します。 この関数を使って文字列を長さでソートする例は以下のようになります:

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}") //sampleEnd }

逆順

コレクションを逆順にしたものを取り出す事も出来ます。 reversed()関数を使います。

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") println(numbers.reversed()) //sampleEnd }

reversed()は要素のコピーを含む新規のコレクションを返します。 だからあとになって元となるコレクションを変更しても、変更前に取得したreversed()の結果には影響を与えません。 (訳注:要素のコピーと言っているが、要素はコピーでは無く同じオブジェクトへの参照だと思う。ここではコレクションが新規に作られると言いたいのだと思われる。)

これとは別のリバースの関数、asReversed()は同じコレクションインスタンスの、 逆順のビューを返すので、元のコレクションが変更されない事が分かっているならreversed()よりも軽量で望ましい場合があります。

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers) //sampleEnd }

オリジナルのリストがミュータブルなら、オリジナルのコレクションへの変更はリバースのビューにも影響を与えるし、リバースのビューへの変更もオリジナルのコレクションに影響を与えます。 (訳注:説明には書いていないが、MutableListのasReversedはMutableListを返す)

fun main() { //sampleStart val numbers = mutableListOf("one", "two", "three", "four") val reversedNumbers = numbers.asReversed() println(reversedNumbers) numbers.add("five") println(reversedNumbers) //sampleEnd }

しかしながら、リストのミュータビリティが不明だったり、そもそも元となるコレクションがリストで無い場合などは、reversed()の方が結果がコピーになって勝手に変わる事が無い事が保証されるので、 より望ましいと言えます。

ランダムな順番

最後に、ランダムな順番の新たなListを返す関数があります ー shuffled()です。 この関数は引数無しで呼び出す事も出来ますし、Randomオブジェクトを引数に呼び出す事も出来ます。

fun main() { //sampleStart val numbers = listOf("one", "two", "three", "four") println(numbers.shuffled()) //sampleEnd }