“This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

preface

Dart supports four types of collections: Lists, Maps, and sets. Queues are defined in the Dart: Collection library.

  • List: the List class, dynamically growing arrays;
  • Key-value set: Map

    class, used to store key-value pairs.
    ,>
  • Queue: the Queue class;
  • Set: The Set class. The elements of the Set cannot be repeated.

This article describes best practices for collections.

The collection specific syntax is preferred

As with the core collection classes List, Map, and Set, Dart provides built-in syntax for these classes to quickly build these collection objects because they are often used.

// Recommended usage
var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int> {};/ / do not recommend
var addresses = Map<String, Address>();
var counts = Set<int> ();Copy the code

Collections also have special uses, such as using the expansion operator (which also supports? Operator to determine if null) adds one collection to another. It also supports the combination of if and for to add control elements.

// Recommended usage
var arguments = [
  ...options,
  command,
  ...?modeFlags,
  for (var path in filePaths)
    if (path.endsWith('.dart'))
      path.replaceAll('.dart'.'.js')];/ / do not recommend
var arguments = <String> []; arguments.addAll(options); arguments.add(command);if(modeFlags ! =null) arguments.addAll(modeFlags);
arguments.addAll(filePaths
    .where((path) => path.endsWith('.dart'))
    .map((path) => path.replaceAll('.dart'.'.js')));
Copy the code

Except for the expansion operator, the recommended uses of if and for are not common. To tell you the truth, I am not used to this writing method, and I feel that the readability is not high.

Do not use the.length attribute to determine whether the collection is empty

Because the collection follows the Iterable protocol, this protocol does not require the collection to know its length at all times. So when YOU call.length, you’re actually walking through it, and it’s very slow. Here’s how to get length:

int get length {
  assert(this is! EfficientLengthIterable);
  int count = 0;
  Iterator it = iterator;
  while (it.moveNext()) {
    count++;
  }
  return count;
}
Copy the code

Therefore, a more efficient way to determine whether the collection isEmpty is to use.isempty or.isnotempty.

bool getisEmpty => ! iterator.moveNext();Copy the code

Therefore, do not use.length == 0 to determine whether the set is empty.

// Correct example
if (lunchBox.isEmpty) return 'so hungry... ';
if (words.isNotEmpty) return words.join(' ');

// Error example
if (lunchBox.length == 0) return 'so hungry... ';
if(! words.isEmpty)return words.join(' ');
Copy the code

Avoid using forEach iteration elements

In JS, the forEacth method is used to iterate over elements because the built-in for-in loop is not what we want. But a for-in loop in Dart is a normal iteration, which simplifies our code.

// Correct example
for (final person in people) {
  ...
}

// Error example
people.forEach((person) {
  ...
});
Copy the code

But if we were to operate on each element, we could pass the operation directly to forEacth as a method, which would be much cleaner.

people.forEach(print);
Copy the code

Note that maps are not iterable, so using forEach is fine.

Do not use list.from () unless you want to change the type of the result

Here are two lines of code for comparison:

var list = ['a'.'b'];

var copy1 = list.toList();
var copy2 = List.from(list);

print(copy1.runtimeType);
print(copy2.runtimeType);
Copy the code

Guess what the printout will look like?

List<String>
List<dynamic>
Copy the code

If you use the list. from method, if you do not specify a generic type, the collection type will be erased and become Dynamic!! Therefore, you should not use the list. from method unless some object requires such a cast. Of course, list. from is not useless, for example, numeric types can be cast, you can specify the type to cast, for example, because the rest of the following are integers, can be cast to List.

Var numbers = [1, 2, 4]; // List<num>. numbers.removeAt(1); // Now it only contains integers. var ints = List<int>.from(numbers);Copy the code

Filter the type using whereType

If you want to filter a subset of a type from a dynamic collection, you should use the whereType

method instead of where to filter.

var list = ['1'.'2'.1.2];

// Correct example
var intList = list.whereType<int> ();// Error example
var intList = list.where((e) => e is int);
Copy the code

This is because the WHERE method still returns a WhereIterableObject, not the desired WhereIterable

Object, which means that using WHERE requires another cast, which is not recommended.

// Error example
var list = ['1'.'2'.1.2];
var intList = list.where((e) => e is int).cast<int> ();Copy the code

Do not cast if you have an alternative

Typically, when dealing with an iterator or stream, we do a series of operations on it. After that, we specify an object of a type. Instead of using the cast() method, we should use other possible conversions. For example, when using toList, we can use List

. From for type conversion.

// Correct example
var stuff = <dynamic> [1.2];
var ints = List<int>.from(stuff);

// Error example
var stuff = <dynamic> [1.2];
var ints = stuff.toList().cast<int> ();Copy the code

We can also use map

to turn a collection into a collection of another type.

// Correct example
var stuff = <dynamic> [1.2];
var reciprocals = stuff.map<double>((n) => 1 / n);

// Error example
var stuff = <dynamic> [1.2];
var reciprocals = stuff.map((n) => 1 / n).cast<double> ();Copy the code

Avoid casting with cast()

When we have no other way to cast, we also need to avoid casting () whenever possible. Here are a few tips to avoid casting:

  • Define the collection type correctly, and if the collection type is explicit, it should be explicit when the collection object is defined. For example:
// Correct example
List<int> singletonList(int value) {
  var list = <int> []; list.add(value);return list;
}

// Error example
List<int> singletonList(int value) {
  var list = []; // List<dynamic>.
  list.add(value);
  return list.cast<int> (); }Copy the code
  • When an element is accessed, and when a collection is iterated, each element can be typed during the iteration.
// Correct example
void printEvens(List<Object> objects) {
  // Suppose we know that sets are only integers
  for (final n in objects) {
    if ((n as int).isEven) print(n); }}// Error example
void printEvens(List<Object> objects) {
  // Suppose we know that sets are only integers
  for (final n in objects.cast<int> ()) {if (n.isEven) print(n); }}Copy the code
  • Preferred to useList.from()Do the transformation. If most of the elements of the collection are accessible and no longer need to be processed before the conversionList.fromLet’s do the transformation. The cast() method returns a collection of deferred processing, and the transformation is performed when the element is needed. This is efficient for converting a small number of elements. In most cases, however, the pitfalls of wrapping an object as a deferred object are more obvious.
// Correct example
int median(List<Object> objects) {
  // Suppose we know that sets are only integers
  var ints = List<int>.from(objects);
  ints.sort();
  return ints[ints.length ~/ 2];
}

// Error example
int median(List<Object> objects) {
  // Suppose we know that sets are only integers
  var ints = objects.cast<int> (); ints.sort();return ints[ints.length ~/ 2];
}
Copy the code

conclusion

This article summarizes some of the best practices for scenarios that use collections in the Dart language. In fact, there are a lot of points that we don’t usually pay attention to — just use it. However, there are guidelines already in place, and knowing what is right will help us write higher-quality code!

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!