Edited at

Dartにおけるリスト(配列)のインスタンス生成による違い

More than 3 years have passed since last update.

Dartではリスト(配列)としてList<E>クラスを用いますが、インスタンス生成の方法によって内部の実装クラスが変化し、挙動に影響します。具体的に言うと、インスタンス生成の時点で、そのリストが「可変長かどうか」でクラスが使い分けられ、可変長でないリストに対してadd()clear()などのリスト長が変化するメソッドを呼び出すとエラーとなります。

というわけでDartにおけるリストのインスタンス生成方法についてまとめました。なお今回の例ではジェネリクスは省略していますが、本来はつけるべきです。


空(長さ0, 可変長)のリスト

単純な可変長配列として使うならこの方法です。

var list = new List();

もしくは

var list = [];

出力結果

[]


空(長さ10, 可変長)のリスト

インデックスが存在するのではじめからlist[0]等のアクセスが可能になります

var list = []..length = 10;

もしくは

var list = new List.generate(10, (i)=>null);

出力結果

[null, null, null, null, null, null, null, null, null, null]


初期値あり(長さ10, 可変長)

任意の値を入れたいのであれば

var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

インデックスにより導出できる値であれば

var list = new List.generate(10, (i)=> i);

出力結果

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


空(長さ10, 固定長)

一つ目の落とし穴がここです。他の言語(例えばJava)ではListのコンストラクタに初期長さを与えるとメモリ確保だけしてくれるような感じですが、Dartで同じことをすると思わぬエラーが出ます。

var list = new List(10);


初期値あり(長さ10, 固定長)

var list = new List.filled(10, 1);

出力結果

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

ここで2つ目の落とし穴です。List.filledList.generateと違い、遅延実行ではなく、与えられたオブジェクトをそのままリストの初期値にします。そのため、参照型のオブジェクトを与えればすべての要素が同一オブジェクトとなります。つまり次のようなことになります。

var list = new List<List>.filled(2, []);

for (var i = 0; i < 2; i++) {
list[i].add(i);
}

出力結果

[[0, 1], [0, 1]]

ちなみに、やりたかったことはこうです。

var list = <List>[[], []];

for (var i = 0; i < 2; i++) {
list[i].add(i);
}

出力結果

[[0], [1]]


まとめ

var list = new List(); // 長さ0, 可変長

var list = new List(10); // 長さ10, 固定長
var list = []; // 長さ0, 可変長
var list = []..length = 10; // 長さ10, 可変長
var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 長さ10, 可変長
var list = new List.generate(10, (i)=>i); // 長さ10, 可変長
var list = new List.filled(10, 0); // 長さ10, 固定長

ちなみにList.fromにはオプション引数にgrowableがあるので、これをfalseにすると固定長として生成することが出来ます。