Preface
Collections are the most common data structure in applications. Dart supports the following four sets, among which the coreList
, Map
andSet
In the basic framework, andQueue
existdart:collection
Library definition.
- List: That is, List class, an array that can grow dynamically;
- key-value set: that is, Map<K, V> class, used to store key-value pairs;
- Queue: that is, Queue class;
- Set: that is, Set class, elements in the set cannot be repeated.
This article introduces collection best practices.
Preferred collection specific syntax
For core collection classesList
, Map
andSet
,Dart provides built-in syntax for these classes to quickly build these collection objects due to frequent use.
// Recommended usagevar points = <Point>[]; var addresses = <String, Address>{}; var counts = <int>{}; // Not recommendedvar addresses = Map<String, Address>(); var counts = Set<int>();
There are some special uses for collections, such as adding a collection to another collection using the expansion operator (and also supports ? operators to determine whether it is empty). It also supports combining if and for to control the addition of elements.
// Recommended usagevar arguments = [ ...options, command, ...?modeFlags, for (var path in filePaths) if (('.dart')) ('.dart', '.js') ]; // Not recommendedvar arguments = <String>[]; (options); (command); if (modeFlags != null) (modeFlags); (filePaths .where((path) => ('.dart')) .map((path) => ('.dart', '.js')));
The recommended usage above is actually not common to use if and for except for the expansion operator. To be honest, I am not used to this writing style and I feel that it is not very readable.
Do not use the .length attribute to determine whether the collection is empty
Since the collection followsIterable
Protocol, this protocol does not require the collection to know its length at any time. Therefore, call.length
When it comes, it is actually equivalent to traversing it once, and the execution speed is very low. This is the acquisitionlength
Implementation method:
int get length { assert(this is! EfficientLengthIterable); int count = 0; Iterator it = iterator; while (()) { count++; } return count; }
Therefore, it is better to judge whether a set is empty or not, to use.isEmpty
or.isNotEmpty
。
bool get isEmpty => !();
therefore,Do not use .length == 0 to determine whether the set is empty.
// Correct Exampleif () return 'so hungry...'; if () return (' '); // Error Exampleif ( == 0) return 'so hungry...'; if (!) return (' ');
Avoid using forEach iterating elements
In JS, the forEacth method is used to iterate over elements, because the built-in for-in loop is different from what we want. But the for-in loop in Dart is a normal iteration, which will simplify our code.
// Correct Examplefor (final person in people) { ... } // Error Example((person) { ... });
But if we want to operate on each element, we can directly pass this operation as a method toforEacth
, such code is more concise.
(print);
NoticeMap
It is not iterable, so useforEach
There is no problem.
Do not use (), unless you want to change the type of result
Here is the code comparing two lines:
var list = ['a', 'b']; var copy1 = (); var copy2 = (list); print(); print();
Guess what the print results will be?
List<String>
List<dynamic>
If usedFor methods, if the generic type is not specified, the type of the collection will be erased and becomes
dynamic
! ! ! Therefore, unless some objects require such type conversion, they should not be usedmethod. certainly,
It is not useless, for example, numerical types support casting, and you can specify the type to casting. For example, because the rest below is all integers, it can be converted to List type ``.
var numbers = [1, 2.3, 4]; // List<num>. (1); // Now it only contains integers. var ints = List<int>.from(numbers);
Use whereType to filter types
If you want to filter a subset of a certain type from a dynamic collection, you should usewhereType<T>
Method, not usewhere
Come to filter.
var list = ['1', '2', 1, 2]; // Correct Examplevar intList = <int>(); // Error Examplevar intList = ((e) => e is int);
This is becausewhere
The method returns aWhereIterable<Object>
object, not what we wantWhereIterable<int>
Object, which means if usedwhere
A cast is also required, which is not recommended.
// Error Examplevar list = ['1', '2', 1, 2]; var intList = ((e) => e is int).cast<int>();
If there is another way, don't use cast to do cast
Usually, when processing an iterative object orstream
When we are, we will do a series of operations on it. After that, we will specify an object of type. Relative to usecast()
Method, we should use other possible conversion methods. For example, when we use toList, we can useList<T>.from
to perform type conversion.
// Correct Examplevar stuff = <dynamic>[1, 2]; var ints = List<int>.from(stuff); // Error Examplevar stuff = <dynamic>[1, 2]; var ints = ().cast<int>();
We can also usemap<T>
to convert a collection into another type of collection.
// Correct Examplevar stuff = <dynamic>[1, 2]; var reciprocals = <double>((n) => 1 / n); // Error Examplevar stuff = <dynamic>[1, 2]; var reciprocals = ((n) => 1 / n).cast<double>();
Avoid cast conversion using cast()
When we have no other way to perform type conversion, we also need to avoid using it as much as possible.cast()
Do type conversion. Here are a few suggestions to avoid casting:
Define the collection type correctly. If the collection type is clear, then the type should be explicit when the collection object is defined. For example, the following example:
// Correct ExampleList<int> singletonList(int value) { var list = <int>[]; (value); return list; } // Error ExampleList<int> singletonList(int value) { var list = []; // List<dynamic>. (value); return <int>(); }
Convert when accessing elements, and when performing collection iteration, each element can be type-converted during the iteration process.
// Correct Examplevoid printEvens(List<Object> objects) { // Suppose we know that the set only has integers for (final n in objects) { if ((n as int).isEven) print(n); } } // Error Examplevoid printEvens(List<Object> objects) { // Suppose we know that the set only has integers for (final n in <int>()) { if () print(n); } }
Priority use()
Make conversion. If most elements of the collection are accessed and no longer need to be processed before conversion, then useTo do the conversion. The cast() method returns a set of delayed processing, and the conversion will be performed only when elements need to be used. This will be efficient for converting small amounts of elements. However, in most cases, the drawback of wrapping an object as a delayed object is more obvious.
// Correct Exampleint median(List<Object> objects) { // Suppose we know that the set only has integers var ints = List<int>.from(objects); (); return ints[ ~/ 2]; } // Error Exampleint median(List<Object> objects) { // Suppose we know that the set only has integers var ints = <int>(); (); return ints[ ~/ 2]; }
Summarize
This article summarizes the best practices for some scenarios of using collections in the Dart language. In fact, we don’t pay attention to many key points in our daily life - just take the attitude of using it as long as it can be used. However, the official official has long been guided by what is correct and knowing what is correct will help us write higher quality code!
The above is a detailed explanation of the detailed tutorial on using the Dart collection in Flutter. For more information about the Flutter Dart collection, please follow my other related articles!