dart series: collection use best practices

brief introduction

There are four sets in dart: Set, List, Map and queues. What should we pay attention to when using these sets? What kind of use is the best way to use it? Let's have a look.

Create a collection using literals

For the commonly used sets, map and List, they have their own parameterless constructors:

  factory Set() = LinkedHashSet<E>;
  external factory Map();

  @Deprecated("Use a list literal, [], or the List.filled constructor instead")
  external factory List([int? length]);

You can see that Set and Map can use constructors. But for List, parameterless constructors are no longer recommended.

For Set and Map, it can be constructed as follows:

var studentMap = Map<String, Student>();
var ages = Set<int>();

However, dart officials recommend directly using literal values to create these collections, as shown below:

var studentMap = <String, Student>{};
var ages = <int>{};

Why? This is because the literal set in dart is very powerful. The set can be constructed and extended through extension operators, if and for statements, as shown below:

var studentList = [
  ...list1,
  student1,
  ...?list2,
  for (var name in list3)
    if (name.endsWith('jack'))
      name.replaceAll('jack', 'mark')
];

Do not use length to determine whether the collection is empty

For the traversable sets of dart, these sets do not store the length information of the set, so if you call the length of the set Length method, which may lead to traversal of the collection, thus affecting performance.

Note that Set and List are traversable, while Map is not.

Therefore, we need to call the of the collection isEmpty and isNotEmpty method to determine whether the collection is empty, which is faster.

if (studentList.isEmpty) print('it is empty');
if (studentList.isNotEmpty) print('it is not empty');

Traversal of traversable objects

Corresponding to the two traversable sets, Set and List, there are two traversal methods, which can be traversed by calling forEach() method or for in, as shown below:

for (final student in studentList) {
  ...
}
studentList.forEach((student) {
  ...
});

dart recommends using for in for both methods.

Of course, if you want to apply the existing function to each element in the collection, forEach is also possible:

studentList.forEach(print);

Note that because the Map is not traversable, the above rules do not apply to the Map.

List.from and Iterable toList

Traversable objects can be converted into lists by calling toList, the same as List From can also convert traversable objects into lists.

So what's the difference between the two?

var list1 = iterable.toList();
var list2 = List.from(iterable);

The difference between the two is Iterable Tolist does not change the type of data in the list From will for instance:

// Creates a List<String>:
var studentList = ['jack', 'mark', 'alen'];

// Prints "List<String>":
print(studentList.toList().runtimeType);

// Prints "List<dynamic>":
print(List.from(studentList).runtimeType);

Of course, you can also use List From to force the type conversion of the created List.

List<String>.from(studentList)

where and whereType

For traversable objects, there are two methods to filter the elements in the collection. They are where and whereType.

For example, if we need to filter the strings in the List, it can be written as follows:

var studentList = ['jack', 'ma', 18, 31];
var students1 = studentList.where((e) => e is String);
var students2 = studentList.whereType<String>();

It seems that there is not much difference between the two, and both can get the results they deserve. However, there is a difference between the two. For where, an iteratable < Object > is returned. In the above example, if we really need to return a String, we also need to case the returned result:

var students1 = studentList.where((e) => e is String).cast<String>();;

So, if you want to return a specific object, remember to use whereType.

Avoid using cast

Cast is usually used to perform type conversion on elements in a collection, but its performance is relatively low. Therefore, cast must be avoided as a last resort.

So how can we convert types without using cast?

A basic principle is to carry out type conversion in advance when building the collection, rather than the overall cast after building the collection.

For example, the following example converts a dynamic type List into an int type List, so we can call List Type conversion during from method:

var stuff = <dynamic>[1, 2];
var ints = List<int>.from(stuff);

If it is a map, you can do this:

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);

For example, if we need to build an int List, we can specify the internal type of the List at the beginning of creation, and then add elements to it:

List<int> singletonList(int value) {
  var list = <int>[];
  list.add(value);
  return list;
}

summary

These are the collection usage best practices in dart.

This article has been included in http://www.flydean.com/30-dart-collection/

Added by rune_sm on Wed, 23 Feb 2022 05:40:21 +0200