Collections
Dart 对列表、集合和映射集合提供了内置支持。要了解更多关于配置集合所包含类型的信息,请查阅泛型。
列表
也许在几乎所有编程语言中最常见的集合就是数组,即对象的有序组。在 Dart 中,数组是 List
对象,所以大多数人直接称它们为列表。
Dart 列表字面量由方括号 []
包围的、逗号分隔的元素列表表示。每个元素通常是一个表达式。这是一个简单的 Dart 列表:
1 | var list = [1, 2, 3]; |
注意
Dart 推断 list
的类型为 List<int>
。如果你尝试向此列表添加非整数对象,分析器或运行时会引发错误。更多信息,请阅读类型推断。
你可以在 Dart 集合字面量的最后一个元素后面添加一个逗号。这个尾随逗号不影响集合,但有助于防止复制粘贴错误。
1 | var list = ['Car', 'Boat', 'Plane']; |
列表使用从零开始的索引,其中 0
是第一个元素的索引,list.length - 1
是最后一个元素的索引。你可以使用 .length
属性获取列表的长度,并使用下标运算符 []
访问列表的元素:
1 | var list = [1, 2, 3]; |
要创建一个编译时常量的列表,请在列表字面量前添加 const
:
1 | var constantList = const [1, 2, 3]; |
有关列表的更多信息,请参阅 dart:core
文档的列表部分。
集合
Dart 中的集合是唯一元素的无序集合。Dart 通过集合字面量和 Set
类型来支持集合。
这是一个简单的 Dart 集合,使用集合字面量创建:
1 | var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'}; |
注意
Dart 推断 halogens
的类型为 Set<String>
。如果你尝试向集合中添加错误类型的元素,分析器或运行时会引发错误。更多信息,请阅读类型推断。
要创建一个空集合,请在 {}
前面使用类型参数,或将 {}
赋值给 Set
类型的变量:
1 | var names = <String>{}; |
是集合还是映射?
映射字面量的语法与集合字面量的语法相似。因为映射字面量出现得更早,所以 {}
默认表示 Map
类型。如果你忘记了在 {}
或其赋值变量上添加类型注解,那么 Dart 会创建一个 Map<dynamic, dynamic>
类型的对象。
使用 add()
或 addAll()
方法向现有集合添加项:
1 | var elements = <String>{}; |
使用 .length
获取集合中的项数:
1 | var elements = <String>{}; |
要创建一个编译时常量的集合,请在集合字面量前添加 const
:
1 | final constantSet = const { |
有关集合的更多信息,请参阅 dart:core
文档的集合部分。
映射
在映射中,每个元素都是一个键值对。每对中的键都与一个值相关联,键和值都可以是任何类型的对象。每个键只能出现一次,但同一个值可以与多个不同的键相关联。Dart 通过映射字面量和 Map
类型来支持映射。
这里有几个简单的 Dart 映射,使用映射字面量创建:
1 | var gifts = { |
注意
Dart 推断 gifts
的类型为 Map<String, String>
,nobleGases
的类型为 Map<int, String>
。如果你尝试向任一映射添加错误类型的值,分析器或运行时会引发错误。更多信息,请阅读类型推断。
你可以使用 Map
构造函数创建相同的对象:
1 | var gifts = Map<String, String>(); |
注意
如果你来自像 C# 或 Java 这样的语言,你可能期望看到 new Map()
而不仅仅是 Map()
。在 Dart 中,new
关键字是可选的。有关详细信息,请参阅使用构造函数。
使用下标赋值运算符 []=
向现有映射添加新的键值对:
1 | var gifts = {'first': 'partridge'}; |
使用下标运算符 []
从映射中检索值:
1 | var gifts = {'first': 'partridge'}; |
如果你查找的键不在映射中,你会得到 null
:
1 | var gifts = {'first': 'partridge'}; |
使用 .length
获取映射中键值对的数量:
1 | var gifts = {'first': 'partridge'}; |
要创建一个编译时常量的映射,请在映射字面量前添加 const
:
1 | final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'}; |
有关映射的更多信息,请参阅 dart:core
文档的映射部分。
集合元素
集合字面量包含一系列元素。在运行时,每个元素都会被求值,产生零个或多个值,然后插入到结果集合中。这些元素分为两大类:叶元素和控制流元素。
叶元素:将单个项插入到集合字面量中。
- 表达式元素:求值单个表达式,并将结果值插入集合。
- 映射条目元素:求值一对键和值表达式,并将结果条目插入集合。
控制流元素:有条件地或迭代地向周围的集合中添加零个或多个值。
- 空感知元素:求值一个表达式,如果结果不为
null
,则将该值插入到周围的集合中。 - 展开元素:遍历给定的序列(集合表达式),并将所有结果值插入到周围的集合中。
- 空感知展开元素:与展开元素类似,但允许集合为
null
,如果为null
则不插入任何内容。 - if 元素:根据给定的条件表达式有条件地求值内部元素,并且如果条件为假,可以选择性地求值另一个
else
元素。 - for 元素:迭代并重复求值给定的内部元素,插入零个或多个结果值。
- 空感知元素:求值一个表达式,如果结果不为
要了解更多关于集合元素的信息,请参阅以下部分。
表达式元素
表达式元素求值单个表达式,并将结果值插入集合。这个表达式可以包含各种结构,如字面量、变量、运算符、函数调用和构造函数调用。
表达式元素在集合中的语法如下:
1 | <expression> |
映射条目元素
映射条目元素求值一对键和值表达式,并将结果条目插入集合。这对中的键和值都可以是表达式。
映射条目元素在集合中的语法如下:
1 | <key_expression>: <value_expression> |
空感知元素
空感知元素求值一个表达式,如果结果不为 null
,则将该值插入到周围的集合中。
版本说明
空感知集合元素需要语言版本至少为 3.8。
空感知元素在表达式元素中的语法如下:
1 | ?<expression> |
空感知元素在映射条目元素中的语法如下:
1 | // 键是空感知元素 |
1 | // 值是空感知元素 |
1 | // 键和值都是空感知元素 |
在下面的例子中,空感知元素 ?a
的结果没有被添加到名为 items
的列表中,因为 a
是 null
:
1 | int? absentValue = null; |
下面的例子说明了在映射条目元素中可以使用空感知元素的各种方式:
1 | String? presentKey = 'Apple'; |
展开元素
展开元素遍历给定的序列,并将所有结果值插入到周围的集合中。
展开元素在集合中的语法如下。序列表达式可以是任何求值为实现了 Iterable
对象的表达式:
1 | ...<sequence_expression> |
在下面的例子中,名为 a
的列表中的元素被添加到了名为 items
的列表中。
1 | var a = [1, 2, null, 4]; |
如果你展开的表达式可能求值为 null
,并且你希望忽略 null
(并且不插入任何元素),请使用空感知展开元素。
要了解更多关于展开运算符的信息,请参阅展开运算符。
空感知展开元素
空感知展开元素与展开元素类似,但它允许集合为 null
,如果为 null
则不插入任何内容。
空感知展开元素在集合中的语法如下:
1 | ...?<sequence_expression> |
在下面的例子中,名为 a
的列表被忽略了,因为它是 null
,但名为 b
的列表中的元素被添加到了名为 items
的列表中。请注意,如果集合本身不为 null
,但它包含 null
元素,那么这些 null
元素仍然会被添加到结果中。
1 | List<int>? a = null; |
由于空安全,你不能对可能为 null
的值执行展开操作 (...
)。下面的例子会产生编译时错误,因为 extraOptions
参数是可空的,并且用在 extraOptions
上的展开运算符不是空感知的。
1 | ✗ static analysis: failure |
如果你想展开一个可空的集合,请使用空感知展开元素。下面的例子是有效的,因为在 extraOptions
上使用了空感知展开运算符。
1 | List<String> buildCommandLine( |
要了解更多关于空感知展开运算符的信息,请参阅展开运算符。
if 元素
if
元素根据给定的条件表达式有条件地求值内部元素,并且如果条件为假,可以选择性地求值另一个 else
元素。
if
元素有几种语法变体:
1 | // 如果布尔表达式为真,则包含结果。 |
1 | // 如果表达式匹配模式,则包含结果。 |
1 | // 如果操作解析为真,则包含第一个结果,否则包含第二个结果。 |
1 | // 如果操作解析为真,则包含第一个结果,否则包含第二个结果。 |
下面的例子说明了在集合中使用带有布尔表达式的 if
元素的各种方式:
1 | var includeItem = true; |
1 | var includeItem = true; |
1 | var name = 'apple'; |
1 | var name = 'apple'; |
下面的例子说明了在集合中使用带有 case
部分的 if
元素的各种方式:
1 | Object data = 123; |
1 | var word = 'hello'; |
1 | var orderDetails = ['Apples', 12, '']; |
你可以将不同的 if
操作与 else if
部分混合使用。例如:
1 | var a = 'apple'; |
要了解更多关于 if
条件语句的信息,请参阅 if
语句。要了解更多关于 if-case
条件语句的信息,请参阅 if-case
语句。
for 元素
for
元素迭代并重复求值给定的内部元素,插入零个或多个结果值。
for
元素在集合中的语法如下:
1 | for (<expression> in <collection>) <result> |
1 | for (<initialization_clause>; <condition_clause>; <increment_clause>) <result> |
下面的例子说明了在集合中使用 for
元素的各种方式:
1 | var numbers = [2, 3, 4]; |
1 | var items = [1, for (var x = 5; x > 2; x--) x, 7]; // [1, 5, 4, 3, 7] |
1 | var items = [1, for (var x = 2; x < 4; x++) x, 7]; // [1, 2, 3, 7] |
要了解更多关于 for
循环的信息,请参阅 for
循环。
嵌套控制流元素
你可以将控制流元素相互嵌套。这是其他语言中列表推导式的一个强大替代方案。
在下面的例子中,只有 numbers
中的偶数被包含在 items
中。
1 | var numbers = [1, 2, 3, 4, 5, 6, 7]; |
在 if
或 for
元素内部立即对集合字面量使用展开是常见且地道的用法。例如:
1 | var items = [ |
你可以任意深度地嵌套各种元素。在下面的例子中,if
、for
和展开元素在集合中相互嵌套:
1 | var nestItems = true; |