详解Flutter中Dart集合使用教程

Elizabeth ·
更新时间:2024-11-10
· 1502 次阅读

目录

前言

优先使用集合的特有语法

不要使用.length 属性判断集合是不是为空

避免使用 forEach 迭代元素

不要使用 List.from(),除非你想要更改结果的类型

使用 whereType 过滤类型

避免使用 cast() 做强制转换

总结

前言

集合是应用程序中最为常见的数据结构,Dart 一共支持如下四种集合,其中核心的 ListMap 和 Set 在基础框架中,而 Queue 在 dart:collection 库定义。

列表:也就是 List类,可动态增长的数组;

key-value 集:即 Map<K, V> 类,用于存储键值对;

队列:即 Queue类;

集合:即Set类,集合中的元素不可重复。

本篇介绍集合的最佳实践。

优先使用集合的特有语法

对于核心的集合类ListMap 和 Set ,由于经常使用,Dart 为这些类提供的内置的语法来快速构建这些集合对象。

// 推荐用法 var points = <Point>[]; var addresses = <String, Address>{}; var counts = <int>{}; // 不推荐 var addresses = Map<String, Address>(); var counts = Set<int>();

集合还有一些特殊的用法,比如使用展开操作符(而且同时支持 ? 操作符判断是否为空)将一个集合加入到另一个集合。同时还支持结合 if 和 for 来控制元素的加入。

// 推荐用法 var arguments = [   ...options,   command,   ...?modeFlags,   for (var path in filePaths)     if (path.endsWith('.dart'))       path.replaceAll('.dart', '.js') ]; // 不推荐 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')));

上面的推荐用法其实除了展开操作符以外,使用 if 和 for 的并不常见。说实话,个人挺不习惯这种写法的,感觉可读性并不高。

不要使用.length 属性判断集合是不是为空

由于集合遵循的是 Iterable 协议,这个协议并不需要集合随时知道它的长度。因此调用.length 的时候,其实相当于是遍历了一遍,执行速度是很低的。这是获取 length 的实现方法:

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

因此,更高效地判断集合是否为空的做法是使用.isEmpty 或 .isNotEmpty

bool get isEmpty => !iterator.moveNext();

因此,不要用.length == 0来判断集合是否为空。

// 正确示例 if (lunchBox.isEmpty) return 'so hungry...'; if (words.isNotEmpty) return words.join(' '); // 错误示例 if (lunchBox.length == 0) return 'so hungry...'; if (!words.isEmpty) return words.join(' '); 避免使用 forEach 迭代元素

在 JS 中,会使用 forEacth 方法来迭代元素,这是因为内置的 for-in 循环和我们想要的不一样。但是在 Dart 中的 for-in 循环是正常的迭代,这样会简化我们的代码。

// 正确示例 for (final person in people) {   ... } // 错误示例 people.forEach((person) {   ... });

但是如果我们是要对每个元素进行操作的话,那么可以直接将这个操作作为方法传递到 forEacth 中,这样的代码更简洁。

people.forEach(print);

注意 Map 是不可迭代的,因此使用 forEach 是没问题的。

不要使用 List.from(),除非你想要更改结果的类型

下面是两行对比的代码:

var list = ['a', 'b']; var copy1 = list.toList(); var copy2 = List.from(list); print(copy1.runtimeType); print(copy2.runtimeType);

猜猜打印出来的结果会是什么?

List<String>
List<dynamic>

如果使用 List.from 方法的话,如果不指定泛型类型,会抹除集合的类型,变成 dynamic!!!因此,除非某些对象需要做这样的类型转换,否则不应该使用 List.from 方法。当然,List.from 也不是没有用,比如数值类型支持强制转换,可以指定类型做强制转换,例如下面剩下的因为都是整数了,因此可以转为 List类型``。

var numbers = [1, 2.3, 4]; // List<num>. numbers.removeAt(1); // Now it only contains integers. var ints = List<int>.from(numbers); 使用 whereType 过滤类型

如果要从动态集合筛选某个类型的子集,那么应该使用 whereType<T>方法,而不是使用 where 来过滤。

var list = ['1', '2', 1, 2]; // 正确示例 var intList = list.whereType<int>(); // 错误示例 var intList = list.where((e) => e is int);

这是因为,where 方法返回的仍然是一个 WhereIterable<Object>对象,而不是我们想要的WhereIterable<int> 对象,这意味如果使用 where 还需要做一次强制转换,这并不推荐。

// 错误示例 var list = ['1', '2', 1, 2]; var intList = list.where((e) => e is int).cast<int>();

如果有别的方式的话,不要使用 cast 做强制转换

通常,当在处理迭代对象或 stream 的时候,我们会对其做一系列的操作。之后,我们会指定一个类型的对象。相对于使用 cast() 方法,我们应该使用其他可能存在的转换方式。例如,当我们使用 toList 的时候,可以使用 List<T>.from 来进行类型转换。

// 正确示例 var stuff = <dynamic>[1, 2]; var ints = List<int>.from(stuff); // 错误示例 var stuff = <dynamic>[1, 2]; var ints = stuff.toList().cast<int>();

我们也可以使用 map<T> 来将集合转为另一个类型的集合。

// 正确示例 var stuff = <dynamic>[1, 2]; var reciprocals = stuff.map<double>((n) => 1 / n); // 错误示例 var stuff = <dynamic>[1, 2]; var reciprocals = stuff.map((n) => 1 / n).cast<double>(); 避免使用 cast() 做强制转换

当我们没有其他办法进行类型转换时,那么也需要尽可能地避免使用 cast() 做类型转换。这里有几条建议能够避免使用强制转换:

正确地定义集合类型,如果集合类型是明确的,那么就应该在集合对象定义时明确类型。例如下面的例子:

// 正确示例 List<int> singletonList(int value) {   var list = <int>[];   list.add(value);   return list; } // 错误示例 List<int> singletonList(int value) {   var list = []; // List<dynamic>.   list.add(value);   return list.cast<int>(); }

在访问元素时进行转换,当进行集合迭代的时候,可以在迭代过程中对每个元素进行类型转换。

// 正确示例 void printEvens(List<Object> objects) {   // 假设我们知道集合只有整数   for (final n in objects) {     if ((n as int).isEven) print(n);   } } // 错误示例 void printEvens(List<Object> objects) {   // 假设我们知道集合只有整数   for (final n in objects.cast<int>()) {     if (n.isEven) print(n);   } }

优先使用 List.from() 做转换。如果集合的大部分元素都会被访问到,而且不再需要对转换前的做处理,那么就使用 List.from 来做转换。cast()方法返回的是一个延迟处理的集合,当需要使用元素时才会执行转换。对于转换少量元素而言,这样效率会高。但是,大部分情况下,将对象包装为延迟对象的缺陷更明显。

// 正确示例 int median(List<Object> objects) {   // 假设我们知道集合只有整数   var ints = List<int>.from(objects);   ints.sort();   return ints[ints.length ~/ 2]; } //  错误示例 int median(List<Object> objects) {   // 假设我们知道集合只有整数   var ints = objects.cast<int>();   ints.sort();   return ints[ints.length ~/ 2]; } 总结

本篇总结了 Dart 语言中使用集合的一些场景的最佳实践,实际上很多要点我们在平时并不会注意 —— 抱着能用就行了的态度。但是,这些内容官方早就有了指引,知道何为正确会有助于我们编写质量更高的代码!

以上就是详解Flutter中Dart集合使用教程的详细内容,更多关于Flutter Dart集合的资料请关注软件开发网其它相关文章!



dart flutter 教程

需要 登录 后方可回复, 如果你还没有账号请 注册新账号