概要
mutableListをListにする時は、必ずしも toList()
を呼ぶ必要はないです
fun returnsImmutableList(): List<Int> {
val mutableList = mutableListOf<Int>()
// (略:mutableListに要素を詰める)
return mutableList.toList() // <- ここで無駄に処理が走る
}
説明
mutableListをListにしたい時にとりあえずtoList()を呼びがちですが、無駄に計算量を増やしている場合があります
本稿ではtoList()の挙動について説明し、その使い所について考えます
ListとMutableListの関係
List
もMutableList
もinterface
で、多くの場合ArrayList
を実体として持つと思います
例えば、mutableListOf()
はArrayListを生成して返す関数です1
そして、MutableList
はList
を継承しています2
つまり、以下のような記述が可能です
val list: List<Int> = mutableListOf<Int>()
toList()
は何をしているか
toList()
はIterable
インターフェースの関数です
その実装をみてみると、実は内部で**toMutableList()
を呼んでいます**3
初めの例ですと、MutableListをListにしようとしたのに、実はMutableListを返しているのです
そのtoMutableList()
は何をしているかというと、ArrayList
のコンストラクタを呼び出して新しいインスタンスを生成します
public fun <T> Collection<T>.toMutableList(): MutableList<T> {
return ArrayList(this)
}
この時、元のCollection
はディープコピーされますので、O(N)の時間/空間計算量かかってしまいます
toList()
の使い所
以上から、toList()
を使う/使わないは次のように判断できます
-
MutableList
の参照だけ渡せれば良い場合 -> List型に安全にキャストできる - ディープコピーが必要な場合 ->
toList()
を使う
具体的に書き下すと以下のような使い分けが良さそうです
toList()
を使わない
- 関数の中では
MutableList
で扱いたいが、List
型として返したい(最初の例)-
MutableList
型のままreturnできます
-
- 関数呼び出しで引数として
List
型を要求されているが、MutableList
を渡したい-
MutableList
型のまま渡せます
-
toList()
を使うべき
-
MutableList
を操作したいが操作前の状態を保持したい時 -
Set
など他のCollection
をList
にする時
以上です
読んでいただきありがとうございました
-
arrayListOf()
で明示的にArrayListを得ることもできます ↩ -
https://qiita.com/opengl-8080/items/36351dca891b6d9c9687#%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%AE%E9%96%A2%E4%BF%82 ↩
-
IterableがCollectionである場合で、かつそのCollectionのsizeが2以上の場合に限ります ↩