tyamaguc07's hatenablog

考えたり調べたりしたことを書いていく。

kotlinの Iterable<T>.distinct() を眺めて気付いた、「実態としての可変」と「型としての不変」

list.toSet().sizelist.distinct().size は同じ結果になる。

この違いは何であろうか?
調べてみるとStack overflowに記事があった。

stackoverflow.com

distinct() は内部的に toSet() しているとのことだ。
ほほーと思い実装を見に行った。

public fun <T> Iterable<T>.distinct(): List<T> {
    return this.toMutableSet().toList()
}

toSet() ではなく toMutableSet() ではあるが、確かにSetに変換することで重複を除去しているようだ。
その後に、toList() をしているので、重複を除外したいのであれば distinct() を使うより toSet() したほうがパフォーマンスが良さそうだ。

もしかしたら、 toList() で効率的な処理がされているのかも?と思い、 toList() の実装を見ると、面白いことに気がついた。*1

戻り値に List<T> を期待しているところで、 MutableList<T> を返しているのだ。

public fun <T> Iterable<T>.toList(): List<T> {
    if (this is Collection) {
        return when (size) {
            0 -> emptyList()
            1 -> listOf(if (this is List) get(0) else iterator().next())
            else -> this.toMutableList()
        }
    }
    return this.toMutableList().optimizeReadOnlyList()
}

MutableList<T>List<T> を継承しているので、この返し方ができるのは分かる。
ただ、不変であることはどのように表現されているのかが気になったのでo1に教えてもらった。

(タイトルの通りだが)結論、インスタンスの実態としては可変リストであり、型としては不変として表現されているので、私達は不変リストとして利用することが出来ているとのことだった。

言語レベルの実装を見ると、このような気付きが得られることが良い😆

*1:面白くて、効率的な処理のことは頭から吹っ飛んだ