Home > Enterprise >  how to explain the output of this Dart code - List of List<int>
how to explain the output of this Dart code - List of List<int>

Time:07-16

Does anyone agree that the output of this code is surprising?

If so, can anyone explain what is happening. Why does add(499) result in three new elements with value 499 instead of just one?

shared[0] has type List<int> so add performed on shared[0] should have no effect on shared[1].

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

Dart program:

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]);
  }
}

CodePudding user response:

shared[0] has type List<int> so add performed on shared[0] should have no effect on shared[1].

The fact that shared[0] is List<int> is orthogonal to the fact that shared[0] and shared[1] are both object references to the same single List<int> that you created on line 1: var xx = <int>[];.


Let's go line-by-line:

  1. var xx = <int>[];

    • This creates a new empty, resizable List<int> object in the program.
      • I'm not a Dart user, but from what I can tell this object will live on Dart's GC heap, just like new List<int>() in C# or new ArrayList<Integer>() in Java - and objects that live on the GC heap can have any number of inbound references from elsewhere in the program.
  2. final shared = List.filled(3, xx);

    • This creates a second new List<List<int>> object, with an initial size of 3, and each of those 3 elements is a reference to the xx object passed into filled.
    • The final modifier just means that the shared variable (an object reference) cannot be re-assigned. It does not mean that shared is an immutable object or in any way read-only, nor does it confer copy-semantics or value-semantics on object-references.
  3. print(shared);

    • This prints "[[], [], []]" because at-this-point, the xx list is still empty.
  4. shared[0].add(499);

  5. shared[1].add(599);

    • Again, this is the same as doing shared[2].add(599) or xx.add(599).
  6. print(shared);

    • This prints "[[499, 599], [499, 599], [499, 599]]".
    • Perhaps instead, think of the print output as "[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]);

    • This does effectively the same thing as print(shared), except rendering each nested element on its own line, thus hiding the the nested structure of the list.
    • If you change the print call-site to something like this: print( "k: $k, j: $j == ${shared[k][j]}"); then you get output that's easier to understand:
      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
      

See for yourself by copying pasting the code below to 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
}

Gives me this output:

[[], [], []]
[[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
  •  Tags:  
  • dart
  • Related