Dart List.filled创建嵌套列表调用add为何子列表同步变化?
运行结果反常的核心原因
这个结果确实违背很多开发者的直觉,问题根源在于对Dart的List.filled构造方法行为理解有误:
List.filled(int length, E fill)的核心逻辑是:创建固定长度为length的列表,所有下标位置存储的都是同一个fill对象的引用,构造过程不会为每个位置生成fill对象的独立副本。- 你的代码中首先创建了一个空列表实例
xx,随后调用List.filled(3, xx)时,shared列表的三个位置shared[0]、shared[1]、shared[2]存储的全是指向这同一个xx实例的引用,并不存在三个独立的子列表。
你对
shared[0]或shared[1]调用add方法时,本质都是在操作内存里那唯一的一个xx列表实例,自然三个下标位置读出来的内容会完全同步变化,看起来就像改一个位置所有子列表都跟着变。
代码执行流程拆解
- 执行
var xx = <int>[];:内存中开辟空间创建一个空的int类型列表,变量xx持有这个实例的引用。 - 执行
final shared = List.filled(3, xx);:创建长度为3的列表shared,三个元素全部存入xx持有的列表引用,此时三个下标指向的都是同一个空列表,第一次打印输出[[], [], []]。 - 执行
shared[0].add(499);:取出shared[0]存储的引用(也就是最开始创建的xx实例),向该列表中添加元素499,此时这个唯一的列表内容变为[499],三个下标读取到的列表内容自然同步更新。 - 执行
shared[1].add(599);:取出shared[1]存储的同一个列表引用,向其中添加元素599,此时列表内容变为[499, 599],第二次打印时三个子列表自然全是这个内容,后续遍历打印的元素顺序也和输出完全匹配。
正确实现方式
如果你的需求是创建包含3个独立空子列表的二维列表,需要使用List.generate构造方法,它会为每个下标单独执行生成逻辑,创建独立的实例:
main() { // 每个下标位置都会生成一个全新的空列表,三个子列表完全独立互不影响 final shared = List.generate(3, (index) => <int>[]); print(shared); // 输出 [[], [], []] shared[0].add(499); shared[1].add(599); print(shared); // 输出 [[499], [599], []],符合常规直觉 }
注意:List.filled仅适合填充int、String、bool这类不可变的值类型。如果要填充列表、自定义类实例等引用类型,除非明确需要所有位置共享同一个实例,否则不要使用List.filled,否则很容易出现这种一改全改的非预期bug。
内容的提问来源于stack exchange,提问作者Alpha
相关产品推荐
相关产品推荐

