详解Flutter中Dart集合使用教程

目录
  • 前言
  • 优先使用集合的特有语法
  • 不要使用.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集合的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android开发Dart Constructors构造函数使用技巧整理

    目录 参考 正文 什么是构造函数? 缺省构造函数 ー Class() 私有构造函数ー Class._() 命名构造函数ー class.Named() 私有命名构造函数ー class._Named () 工厂构造函数ー factory class Class() 参考 https://dart.dev/guides/language/language-tour#factory-constructors https://www.freecodecamp.org/news/constructors-i

  • Google Dart编程语法和基本类型学习教程

    1. 变量声明 如何定义变量 复制代码 代码如下: var name = 'Bob'; 变量的初始值 复制代码 代码如下: int lineCount;assert(lineCount == null); // Variables (even numbers) are initially null. 可以使用var,也可以直接指定类型.final, 定义为final的变量,值不能够被更改 复制代码 代码如下: final name = 'Bob'; // Or: final String nam

  • 谷歌Sky语言怎么样?什么是Dart编程语言?

    Q:谷歌Sky语言怎么样?Sky编程语言有哪些优势? A:Sky语言是谷歌近期推出的一款全新的自主网页编程语言Dart,谷歌推出Dart编程语言的目的是为了提升Android应用的流畅度.Sky语言的主要优势在于:可兼容iOS,为Android应用带来120fps的超级流畅体验. 作为当前市占率最高的智能手机操作系统,Android平台正在吸引着越来越多的开发者.不过,对用户而言,Android的体验还不够完善,卡顿的情况时有发生.再深入点理解,许多应用的帧率达不到普遍意义上流畅的标准60fps

  • Flutter 中 Dart的Mixin示例详解

    原文在这里.写的不错,推荐各位看原文. 这里补充一下Mixin的定义: 只要一个类是继承自Object的而且没有定义构造方法,那么这个类可以是一个Mixin了.当然,如果你想让mixin的定义更加的清晰,可以使用mixin关键字开头来定义.具体请参考这里 原文截图体会一下风格. 正文 在经典的面向对象编程语言里一定会有常规的类,抽象类和接口.当然,Dart也有它自己的接口,不过那是另外的文章要说的.有的时候阴影里潜伏者另外的野兽:Mixin!这是做什么的,如何使用?我们来一起发现. 没有mixi

  • 详解Flutter和Dart取消Future的三种方法

    目录 使用异步包(推荐) 完整示例 使用 timeout() 方法 快速示例 将Future转换为流 快速示例 结论 使用异步包(推荐) async包由 Dart 编程语言的作者开发和发布.它提供了dart:async风格的实用程序来增强异步计算.可以帮助我们取消Future的是CancelableOperation类: var myCancelableFuture = CancelableOperation.fromFuture( Future<T> inner, { FutureOr on

  • Android开发Dart语言7个很酷的特点

    目录 参考 正文 Cascade 级联 Abstract 抽象类 Factory constructors 工厂建造者 Named 命名构造函数 Mixins 混合物 Typedefs Extension 扩展方法 unawaited_futures 参考 https://dart.dev/guides/language/language-tour 正文 今天的文章简短地揭示了 Dart 语言所提供的很酷的特性.更多时候,这些选项对于简单的应用程序是不必要的,但是当你想要通过简单.清晰和简洁来改

  • 详解Flutter中Dart集合使用教程

    目录 前言 优先使用集合的特有语法 不要使用.length 属性判断集合是不是为空 避免使用 forEach 迭代元素 不要使用 List.from(),除非你想要更改结果的类型 使用 whereType 过滤类型 避免使用 cast() 做强制转换 总结 前言 集合是应用程序中最为常见的数据结构,Dart 一共支持如下四种集合,其中核心的 List, Map 和 Set 在基础框架中,而 Queue 在 dart:collection 库定义. 列表:也就是 List类,可动态增长的数组: k

  • 详解Flutter中视频播放器插件的使用教程

    目录 创建一个新的视频播放器 添加播放和暂停按钮 创建一个快进 添加一个视频进度指示器 应用视频的字幕 结论 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序.电影应用程序和体育相关的应用程序.您是否想知道如何将视频内容添加到您的下一个Flutter应用程序中? 从头开始实现视频功能将是一项繁重的任务.但有几个插件可以让开发者的生活变得轻松.视频播放器插件是可用于 Flutter 的最佳插件之一,可满足这一要求. 在这篇文章中,您将学习如何应用视频播放器插件以及控制视频播放器

  • 详解Flutter中网络框架dio的二次封装

    其实dio框架已经封装的很好了,但是在实战项目中,为了项目可以统一管理,还是需要对dio框架进行二次封装. 整体思路:一般情况下,后台返回的数据我们可以分为两部分 1.状态数据 2.渲染数据 状态数据就是接口有没有正常返回数据相关的数据,这部分数据跟业务无关,我们可以封装起来统一管理,渲染数据就是我们渲染页面所需要的数据,这块的数据需要我们自己处理. 接下来我们就主要处理渲染数据这块的内容,我定义了两个函数,渲染数据可能为一个对象或者一个数组,我做了分别处理,定义两个函数来接受渲染数据. //

  • 详解Flutter中数据传递的方式

    目录 1.构造方法传递 2.InheritedWidget 3.Notification 4.Stream & event_bus 在Flutter中,常见的数据传递一共有以下几种: 1.构造方法传递 Flutter的构造方法具备着dart语言的特点,参数具备可选状态,通过构造方法传递数据,可以很方便的将任意数据进行传递,平时开发中,A跳转B页面最常用的方法就是通过构造方法进行传递.比如我们最常见的Key就是通过构造一级一级向下传递的. 优点: 相邻页面之间传递数据非常方便,你不需要进行任何额外

  • 详解Flutter中StatefulBuilder组件的使用

    目录 例子 预览 编码 结论 本文是关于 Flutter 中的 StatefulBuilder 小部件.我将介绍小部件的基础知识,然后检查一个在实际中使用它的完整示例..StatefulBuilder 小部件可以在这些区域的状态发生变化时仅重建某些小区域而无需付出太多努力.这提高了应用程序的性能. StatefulBuilder({ Key? key, required StatefulWidgetBuilder builder r}) builder 函数有两个参数:context和一个用于在

  • 详解Vuex中getters的使用教程

    目录 简介 说明 官网 getters概述 说明 来源 用法 示例 测试 简介 说明 本文用示例介绍Vuex的五大核心之一:getters. 官网 Getter | Vuex API 参考 | Vuex getters概述 说明 getters 是Store的计算属性,可以对State进行计算操作.就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算. 虽然组件内也可以做计算属性,但getters 可以在多组件之间复用.如果一个状态只在一个

  • 详解Flutter中的数据传递

    目录 Flutter 中的数据传递 InheritedWidget EventBus 总结 Flutter 中的数据传递 在开发中,数据从一个页面传递到另一个页面事很常用的,在Android 开发中,通常是通过把数据放到 intent 中传递过去.在 Flutter 中,数据是如何传递的呢? 在Flutter 中一切都是Widget,所以数据的传递就成了数据才Widget 中的传递.在之前的学习中,数据从一个Widget 传递到 子 Widget 是通过构造函数,一层一层的往里面传,要是 wid

  • 详解flutter中常用的container layout实例

    目录 简介 Container的使用 旋转Container Container中的BoxConstraints 总结 简介 在上一篇文章中,我们列举了flutter中的所有layout类,并且详细介绍了两个非常常用的layout:Row和Column. 掌握了上面两个基本的layout还是不够的,如果需要应付日常的layout使用,我们还需要掌握多一些layout组件.今天我们会介绍一个功能强大的layout:Container layout. Container的使用 Container是一

  • 详解MongoDB中创建集合与删除集合的操作方法

    创建集合:createCollection() 方法 MongoDB db.createCollection(name, options) 是用来创建集合. 语法: 基本的 createCollection() 命令语法如下: db.createCollection(name, options) 在命令中, name 是要创建的集合的名称. Options 是一个文件,用于指定配置的集合 参数 类型 描述 Name String 要创建的集合名称 Options Document (可选)指定有

随机推荐