集合
Dart 内置支持列表 (list)、集合 (set) 和映射 (map) 集合。要了解有关配置集合中包含的类型,请查阅泛型。
列表
#在几乎所有编程语言中,最常见的集合可能是数组,或称有序对象组。在 Dart 中,数组是 List
对象,因此大多数人直接称它们为列表。
Dart 列表字面量由用方括号 ([]
) 括起来的逗号分隔的元素列表表示。每个元素通常是一个表达式。以下是一个简单的 Dart 列表:
var list = [1, 2, 3];
您可以在 Dart 集合字面量中的最后一项后添加逗号。这个尾随逗号不会影响集合,但有助于防止复制粘贴错误。
var list = ['Car', 'Boat', 'Plane'];
列表使用基于零的索引,其中 0 是第一个元素的索引,list.length - 1
是最后一个元素的索引。您可以使用 .length
属性获取列表的长度,并使用下标运算符 ([]
) 访问列表的元素。
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);
list[1] = 1;
assert(list[1] == 1);
要创建编译时常量列表,请在列表字面量前添加 const
。
var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.
有关列表的更多信息,请参阅 dart:core
文档中的列表部分。
集合
#Dart 中的集合 (set) 是唯一元素的无序集合。Dart 通过集合字面量和 Set
类型提供对集合的支持。
以下是一个使用集合字面量创建的简单 Dart 集合:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
要创建一个空集合,请使用 {}
并在其前加上类型参数,或将 {}
赋值给 Set
类型的变量。
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.
使用 add()
或 addAll()
方法向现有集合添加项:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
使用 .length
获取集合中的项数:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);
要创建编译时常量集合,请在集合字面量前添加 const
。
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium'); // This line will cause an error.
有关集合的更多信息,请参阅 dart:core
文档中的集合部分。
映射
#在映射中,每个元素都是一个键值对。键值对中的每个键都与一个值关联,并且键和值都可以是任何类型的对象。每个键只能出现一次,尽管相同的值可以与多个不同的键关联。Dart 通过映射字面量和 Map
类型提供对映射的支持。
以下是几个使用映射字面量创建的简单 Dart 映射:
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings',
};
var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};
您可以使用映射构造函数创建相同的对象:
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
使用下标赋值运算符 ([]=
) 向现有映射添加新的键值对:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair
使用下标运算符 ([]
) 从映射中检索值:
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');
如果您查找映射中不存在的键,将返回 null
。
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);
使用 .length
获取映射中的键值对数量:
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);
要创建编译时常量映射,请在映射字面量前添加 const
。
final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'};
// constantMap[2] = 'Helium'; // This line will cause an error.
有关映射的更多信息,请参阅 dart:core
文档中的映射部分。
集合元素
#集合字面量包含一系列元素。在运行时,每个元素都会被评估,产生零个或多个值,然后这些值被插入到结果集合中。这些元素分为两大类:叶元素和控制流元素。
叶元素:将单个项插入到集合字面量中。
表达式元素:评估单个表达式并将结果值插入到集合中。
映射条目元素:评估一对键和值表达式并将结果条目插入到集合中。
控制流元素:有条件地或迭代地向周围集合添加零个或多个值。
空感知元素:评估一个表达式,如果结果不为
null
,则将该值插入到周围集合中。展开元素:迭代给定序列(集合表达式)并将所有结果值插入到周围集合中。
空感知展开元素:与展开元素类似,但允许集合为
null
,如果为null
则不插入任何内容。If 元素:根据给定的条件表达式有条件地评估一个内部元素,如果条件为假,则可选地评估另一个
else
元素。For 元素:迭代并重复评估给定的内部元素,插入零个或多个结果值。
要了解有关集合元素的更多信息,请参阅以下部分。
表达式元素
#表达式元素评估单个表达式并将结果值插入到集合中。此表达式可以包含各种构造,如字面量、变量、运算符、函数调用和构造函数调用。
表达式元素在集合中具有以下语法:
<expression>
映射条目元素
#映射条目元素评估一对键和值表达式并将结果条目插入到集合中。该对中的键和值都可以是表达式。
映射条目元素在集合中具有以下语法:
<key_expression>: <value_expression>
空感知元素
#空感知元素评估一个表达式,如果结果不为 null
,则将该值插入到周围集合中。
空感知元素在表达式元素中具有以下语法:
?<expression>
空感知元素在映射条目元素中具有以下语法:
// key is a null-aware element
?<key_expression>: <value_expression>
// value is a null-aware element
<key_expression>: ?<value_expression>
// key and value are null-aware elements
?<key_expression>: ?<value_expression>
在以下示例中,空感知元素 ?a
的结果不会添加到名为 items
的列表中,因为 a
为 null
。
int? absentValue = null;
int? presentValue = 3;
var items = [
1,
?absentValue,
?presentValue,
absentValue,
5,
]; // [1, 3, null, 5]
以下示例说明了在映射条目元素内部使用空感知元素的各种方式:
String? presentKey = 'Apple';
String? absentKey = null;
int? presentValue = 3;
int? absentValue = null;
var itemsA = {presentKey: absentValue}; // {Apple: null}
var itemsB = {presentKey: ?absentValue}; // {}
var itemsC = {absentKey: presentValue}; // {null: 3}
var itemsD = {?absentKey: presentValue}; // {}
var itemsE = {absentKey: absentValue}; // {null: null}
var itemsF = {?absentKey: ?absentValue}; // {}
展开元素
#展开元素迭代给定序列并将所有结果值插入到周围集合中。
展开元素在集合中具有以下语法。序列表达式可以表示评估为实现 Iterable
接口的对象的任何表达式。
...<sequence_expression>
在以下示例中,名为 a
的列表中的元素被添加到名为 items
的列表中。
var a = [1, 2, null, 4];
var items = [0, ...a, 5]; // [0, 1, 2, null, 4, 5]
如果您要展开一个可能评估为 null
的表达式,并且希望忽略该 null
(且不插入任何元素),请使用空感知展开元素。
要了解有关展开运算符的更多信息,请参阅展开运算符。
空感知展开元素
#空感知展开元素与展开元素类似,但允许集合为 null
,如果为 null
则不插入任何内容。
空感知展开元素在集合中具有以下语法:
...?<sequence_expression>
在以下示例中,名为 a
的列表被忽略,因为它为 null,但名为 b
的列表中的元素被添加到名为 items
的列表中。请注意,如果集合本身不为 null
,但包含为 null
的元素,则这些 null
元素仍将添加到结果中。
List<int>? a = null;
var b = [1, null, 3];
var items = [0, ...?a, ...?b, 4]; // [0, 1, null, 3, 4]
由于空安全,您不能对可能为 null 的值执行展开操作 (...
)。以下示例将产生编译时错误,因为 extraOptions
参数可为空,并且在 extraOptions
上使用的展开运算符不是空感知的。
List<String> buildCommandLine(
String executable,
List<String> options, [
List<String>? extraOptions,
]) {
return [
executable,
...options,
...extraOptions, // <-- Error
];
}
// Usage:
// buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
// Compile-time error
如果您想展开一个可为空的集合,请使用空感知展开元素。以下示例有效,因为在 extraOptions
上使用了空感知展开运算符。
List<String> buildCommandLine(
String executable,
List<String> options, [
List<String>? extraOptions,
]) {
return [
executable,
...options,
...?extraOptions, // <-- OK now.
];
}
// Usage:
// buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
// [dart, run, my_script.dart]
要了解有关空感知展开运算符的更多信息,请参阅展开运算符。
If 元素
#if
元素根据给定的条件表达式有条件地评估一个内部元素,如果条件为假,则可选地评估另一个 else
元素。
if
元素有几种语法变体:
// If the bool expression is true, include the result.
if (<bool_expression>) <result>
// If the expression matches the pattern, include the result.
if (<expression> case <pattern>) <result>
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<bool_expression>) <result> else <result>
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<expression> case <pattern>) <result> else <result>
以下示例说明了在集合中使用带有布尔表达式的 if
元素的各种方式:
var includeItem = true;
var items = [0, if (includeItem) 1, 2, 3]; // [0, 1, 2, 3]
var includeItem = true;
var items = [0, if (!includeItem) 1, 2, 3]; // [0, 2, 3]
var name = 'apple';
var items = [0, if (name == 'orange') 1 else 10, 2, 3]; // [0, 10, 2, 3]
var name = 'apple';
var items = [
0,
if (name == 'kiwi') 1 else if (name == 'pear') 10,
2,
3,
]; // [0, 2, 3]
以下示例说明了在集合中使用带有 case
部分的 if
元素的各种方式:
Object data = 123;
var typeInfo = [
if (data case int i) 'Data is an integer: $i',
if (data case String s) 'Data is a string: $s',
if (data case bool b) 'Data is a boolean: $b',
if (data case double d) 'Data is a double: $d',
]; // [Data is an integer: 123, Data is a double: 123]
var word = 'hello';
var items = [
1,
if (word case String(length: var wordLength)) wordLength,
3,
]; // [1, 5, 3]
var orderDetails = ['Apples', 12, ''];
var summary = [
'Product: ${orderDetails[0]}',
if (orderDetails case [_, int qty, _]) 'Quantity: $qty',
if (orderDetails case [_, _, ''])
'Delivery: Not Started'
else
'Delivery: In Progress',
]; // [Product: Apples, Quantity: 12, Delivery: Not Started]
您可以将不同的 if
操作与 else if
部分混合使用。例如:
var a = 'apple';
var b = 'orange';
var c = 'mango';
var items = [
0,
if (a == 'apple') 1 else if (a case 'mango') 10,
if (b case 'pear') 2 else if (b == 'mango') 20,
if (c case 'apple') 3 else if (c case 'mango') 30,
4,
]; // [0, 1, 30, 4]
要了解有关 if
条件的更多信息,请参阅if
语句。要了解有关 if-case
条件的更多信息,请参阅if-case
语句。
For 元素
#for
元素迭代并重复评估给定的内部元素,插入零个或多个结果值。
for
元素在集合中具有以下语法:
for (<expression> in <collection>) <result>
for (<initialization_clause>; <condition_clause>; <increment_clause>) <result>
以下示例说明了在集合中使用 for
元素的各种方式:
var numbers = [2, 3, 4];
var items = [1, for (var n in numbers) n * n, 7]; // [1, 4, 9, 16, 7]
var items = [1, for (var x = 5; x > 2; x--) x, 7]; // [1, 5, 4, 3, 7]
var items = [1, for (var x = 2; x < 4; x++) x, 7]; // [1, 2, 3, 7]
要了解有关 for
循环的更多信息,请参阅for
循环。
嵌套控制流元素
#您可以将控制流元素相互嵌套。这是一种替代其他语言中列表推导式的强大方法。
在以下示例中,只有 numbers
中的偶数包含在 items
中。
var numbers = [1, 2, 3, 4, 5, 6, 7];
var items = [
0,
for (var n in numbers)
if (n.isEven) n,
8,
]; // [0, 2, 4, 6, 8]
在 if
或 for
元素内部立即对集合字面量使用展开操作是常见且符合习惯的做法。例如:
var items = [
if (condition) oneThing(),
if (condition) ...[multiple(), things()],
]; // [oneThing, [multiple_a, multiple_b], things]
您可以任意深度地嵌套各种元素。在以下示例中,if
、for
和展开元素在集合中相互嵌套:
var nestItems = true;
var ys = [1, 2, 3, 4];
var items = [
if (nestItems) ...[
for (var x = 0; x < 3; x++)
for (var y in ys)
if (x < y) x + y * 10,
],
]; // [10, 20, 30, 40, 21, 31, 41, 32, 42]