This is not an official answer and I hope one with better knowledge can correct me if I am wrong.
In short, both should result in the same performance and behavior.
We can observe this using --disassemble
with the Dart VM 2.17.5, where it looks like [...list]
is being translated into List.of(list1)
. The List.of
is defined here:
@patch
factory List.of(Iterable<E> elements, {bool growable: true}) {
if (growable) {
return _GrowableList.of(elements);
} else {
return _List.of(elements);
}
}
https://github.com/dart-lang/sdk/blob/9ad08f1c951ae8b5ee0387306f450afaccf4eaba/sdk/lib/_internal/vm/lib/array_patch.dart#L48-L55
Since we can only create growable lists with the []
syntax, then we follow the track into _GrowableList.of
which is defined as:
factory _GrowableList.of(Iterable<T> elements) {
if (elements is _GrowableList) {
return _GrowableList._ofGrowableList(unsafeCast(elements));
}
if (elements is _Array) {
return _GrowableList._ofArray(unsafeCast(elements));
}
if (elements is EfficientLengthIterable) {
return _GrowableList._ofEfficientLengthIterable(unsafeCast(elements));
}
return _GrowableList._ofOther(elements);
}
https://github.com/dart-lang/sdk/blob/eb17dad60440daeb5998751db8311d82769cfb64/sdk/lib/_internal/vm/lib/growable_array.dart#L142-L153
So as we can see, Dart will try and check if the provided Iterable
is actually some other specific type. The common thing here is that we prefer to know the final size of the created new List
but Iterable
cannot safely, by default, be checked for its length without iterating over all elements in the Iterable
. So if we know the Iterable
is actually a type where we safely can get the length
, without any iteration, we should use that size.
The toList()
method is implemented differently for different types of object in Dart and is therefore a way to have an efficient way to make a list from that object. E.g. if we have an _GrowableList
, the toList()
for that can be found here:
List<T> toList({bool growable: true}) {
// ...
final length = this.length;
if (growable) {
if (length > 0) {
final data = new _List(_adjustedCapacity(length));
for (int i = 0; i < length; i++) {
data[i] = this[i];
}
final result = new _GrowableList<T>._withData(data);
result._setLength(length);
return result;
}
return <T>[];
} else {
if (length > 0) {
final list = new _List<T>(length);
for (int i = 0; i < length; i++) {
list[i] = this[i];
}
return list;
}
return List<T>.empty(growable: false);
}
}
https://github.com/dart-lang/sdk/blob/eb17dad60440daeb5998751db8311d82769cfb64/sdk/lib/_internal/vm/lib/growable_array.dart#L509-L540
And we can see in this implementation that since it is already a _GrowableList
, it knows it can get the length
efficiently and then uses that in its implementation for creating a new list based.