概要
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以上の場合に限ります ↩