如何解释此 Dart 代码的输出 - 列表<int>列表



有人同意这段代码的输出令人惊讶吗?

如果是这样,谁能解释一下正在发生的事情。 为什么add(499)会产生三个具有价值499的新元素,而不仅仅是一个?

shared[0]具有类型List<int>因此对shared[0]执行的添加应该对shared[1]没有影响。

// output is
[[], [], []]
[[499, 599], [499, 599], [499, 599]]
499
599
499
599
499
599

飞镖程序:

main()
{
var xx = <int>[];
final shared = List.filled(3, xx);
print(shared);
shared[0].add(499);
shared[1].add(599);
print(shared);
for (int k = 0; k < shared.length; ++k) {
for (int j = 0; j < shared[k].length; ++j)
print(shared[k][j]);
}
}

shared[0]具有类型List<int>因此对shared[0]执行的添加应该对shared[1]没有影响。

shared[0]List<int>的事实与以下事实正交:shared[0]shared[1]都是对您在第 1 行创建的同一单个List<int>的对象引用var xx = <int>[];


让我们逐行进行:

  1. var xx = <int>[];

    • 这会在程序中创建一个新的空的、可调整大小的List<int>对象。
      • 我不是 Dart 用户,但据我所知,这个对象将存在于 Dart 的 GC 堆上,就像 C# 中的new List<int>()或 Java 中的new ArrayList<Integer>()一样 - 并且存在于 GC 堆上的对象可以具有来自程序其他地方的任意数量的入站引用。
  2. final shared = List.filled(3, xx);

    • 这将创建第二个新的List<List<int>>对象,初始大小为3,这 3 个元素中的每一个都是对传递到filledxx对象的引用。
    • final修饰符只是意味着不能重新分配shared变量(对象引用)。这并不意味着shared是不可变的对象或以任何方式只读,也不赋予对象引用复制语义或值语义。
  3. print(shared);

    • 这将打印"[[], [], []]",因为此时,xx列表仍然是空的。
  4. shared[0].add(499);

    • 此时,xx列表仍为空,因此调用.add(499)会将第一个值添加到xx中。
    • 如果你用调试器查看,这将立即在shared[1]shared[2]xx中可见。
    • 证明所有这些对象引用都指向同一对象的另一种方法是使用identical()
  5. shared[1].add(599);

    • 同样,这与执行shared[2].add(599)xx.add(599)相同。
  6. print(shared);

    • 这打印"[[499, 599], [499, 599], [499, 599]]"。
    • 也许相反,将打印输出视为"[xx, xx, xx]"。
  7. for (int k = 0; k < shared.length; ++k) for (int j = 0; j < shared[k].length; ++j) print(shared[k][j]);

    • 这实际上与print(shared)相同,除了将每个嵌套元素呈现在自己的行上,从而隐藏列表的嵌套结构。
    • 如果将print调用站点更改为如下所示的内容:print( "k: $k, j: $j == ${shared[k][j]}");,您将获得更容易理解的输出:
      k: 0, j: 0 == 499
      k: 0, j: 1 == 599
      k: 1, j: 0 == 499
      k: 1, j: 1 == 599
      k: 2, j: 0 == 499
      k: 2, j: 1 == 599
      

通过复制+粘贴以下代码来 Dartpad.dev,请亲自查看:

main()
{
var xx = <int>[];
final shared = List.filled(3, xx);
print(shared);
shared[0].add(499);
shared[1].add(599);
print(shared);

print("");
for (int k = 0; k < shared.length; ++k) {
for (int j = 0; j < shared[k].length; ++j) {
print( "k: $k, j: $j == ${shared[k][j]}");
}
}
print("");

var areReferencesToSameObject_xx_0 = identical(xx, shared[0]);
var areReferencesToSameObject_xx_1 = identical(xx, shared[1]);
var areReferencesToSameObject_xx_2 = identical(xx, shared[2]);
var areReferencesToSameObject_1_2  = identical(shared[1], shared[2]);
var areReferencesToSameObject_xx_shared = identical(xx, shared);

print("xx and shared[0] are the same object: $areReferencesToSameObject_xx_0"); // true
print("xx and shared[1] are the same object: $areReferencesToSameObject_xx_1"); // true
print("xx and shared[2] are the same object: $areReferencesToSameObject_xx_2"); // true
print("shared[1] and shared[2] are the same object: $areReferencesToSameObject_1_2"); // true
print("shared and xx are the same object: $areReferencesToSameObject_xx_shared"); // false
}

给我这个输出:

[[], [], []]
[[499, 599], [499, 599], [499, 599]]
k: 0, j: 0 == 499
k: 0, j: 1 == 599
k: 1, j: 0 == 499
k: 1, j: 1 == 599
k: 2, j: 0 == 499
k: 2, j: 1 == 599
xx and shared[0] are the same object: true
xx and shared[1] are the same object: true
xx and shared[2] are the same object: true
shared[1] and shared[2] are the same object: true
shared and xx are the same object: false

最新更新