集合概述
Dart 中的集合数据类型(Collection)包括 List、Set、Map 和 Queue。
classDiagram
Iterable~E~ <|.. List~E~
Iterable~E~ <|.. Set~E~
Iterable~E~ <|.. Queue~E~
class Iterable{
<<abstract mixin>>
+ Iterator~E~ get iterator
}
<<interface>> List
<<interface>> Set
<<interface>> Queue
Iterable~E~ ..> Iterator~E~ : Creates
Iterable <-- Map~K,V~
class Iterator {
<<interface>>
+ E get current
+ bool moveNext()
}
class Map{
<<interface>>
+ Iterable~MapEntry<K, V>~ get entries
+ Iterable~K~ get keys
+ Iterable~V~ get values
}
Iterable
Iterable(可迭代对象)是一个抽象类,它是所有集合类(如 List 和 Set)的基石。点击这里查看 Iterable生成器。
Iterable 的大多数方法(如 map 和 where)都是惰性的。这意味着当你调用这些方法时,它们并不会立即遍历集合。只有当你最终调用 toList()、toSet() 或在 for-in 循环中使用它时,计算才会真正发生。这种机制在处理大数据集时非常高效,因为它能避免不必要的中间计算。
核心属性
first: 返回第一个元素。last: 返回最后一个元素。single: 检查是否只有一个元素并返回它。isEmpty: 集合是否为空。isNotEmpty: 集合是否不为空。length: 返回元素个数。iterator: 获取用于遍历的 Iterator 对象。
过滤
where(test): 返回满足条件的元素集合。whereType<T>(): 筛选出指定类型的元素(如list.whereType<String>())。take(n): 获取前 n 个元素。takeWhile(test): 从头开始取,直到条件不满足。skip(n): 跳过前 n 个元素。skipWhile(test): 从头开始跳过,直到条件不满足。
转换
map(toElement): 将每个元素转换为另一种形式。expand(toElements): 将每个元素转换为一个序列,然后展平。cast<R>(): 将 Iterable 强制转换为另一种类型的 Iterable。followedBy(other): 将另一个集合接在当前集合后面。toList({growable: true}): 转换为 List(触发惰性求值)。toSet(): 转换为 Set(去重并触发惰性求值)。toString(): 返回集合的字符串表示。
查找
firstWhere(test, {orElse}): 查找第一个满足条件的元素。lastWhere(test, {orElse}): 查找最后一个满足条件的元素。singleWhere(test, {orElse}): 查找唯一满足条件的元素。elementAt(index): 获取指定索引处的元素。contains(element): 判断是否包含某个元素。
逻辑检查
any(test): 是否至少有一个元素满足条件。every(test): 是否所有元素都满足条件。
聚合
fold(initialValue, combine): 提供初始值,并从左到右迭代合并。reduce(combine): 无初始值,将集合元素两两合并(要求集合非空)。join(separator): 将所有元素转为字符串并用分隔符连接。
List
List 是一组有序的对象(元素)的集合,可以通过下标(从 0 开始的整数)获取对应位置的元素。
// ex621.dart
void main() {
var letters = ['A', 'B', 'C', 'A'];
assert(4 == letters.length);
assert('A' == letters[0]);
assert('B' == letters[1]);
assert('C' == letters[2]);
print(letters); // Output: [A, B, C, A]
print(letters.toSet()); // Output: {A, B, C}
}
Set
Set 不关心元素的排序顺序,它关注的是元素是否在集合里,Set 中的元素不会重复。
// ex622.dart
void main() {
var letters = {'A', 'B', 'C', 'A'};
assert(3 == letters.length); // 1
print(letters); // 2 Output: {A, B, C}
print(letters.toList()); // 2a Output: [A, B, C]
}
letters的长度为 3 而不是 4;letters包含的 3 个元素即{A, B, C},A只出现了一次。
Queue
Queue 表示队列,它提供了队列两端的添加/移除操作。
// ex623.dart
import 'dart:collection';
void main() {
var q = Queue.of(['A', 'B', 'C']);
print(q); // Output: {A, B, C}
q.addFirst('X');
print(q); // Output: {X, A, B, C}
q.addLast('Y');
print(q); // Output: {X, A, B, C, Y}
q.removeLast();
print(q); // Output: {X, A, B, C}
q.removeFirst();
print(q); // Output: {A, B, C}
}
Map
Map 是键值(key-value)对的集合,它表达了 key 到 value 的映射关系。
// ex624.dart
void main() {
var prices = {'Apple': 1.2, 'Banana': 1.4, 'Cherry': 2.1};
print(prices.runtimeType); // Output: _Map<String, double>
print(prices['Apple']); // Output: 1.2
prices['Apple'] = 1.5;
prices['Damson'] = .5;
print(prices); // Output: {Apple: 1.5, Banana: 1.4, Cherry: 2.1, Damson: 0.5}
print(prices.keys); // Output: (Apple, Banana, Cherry, Damson)
print(prices.values); // Output: (1.5, 1.4, 2.1, 0.5)
print(prices.entries);
// Output: (MapEntry(Apple: 1.5), MapEntry(Banana: 1.4), MapEntry(Cherry: 2.1), MapEntry(Damson: 0.5))
}
集合元素
以 List 为例,简单说明下集合元素。在以[a, b, ...]这样的语法书写 List 时,其中的a、b等称为元素(elements);每个元素将被求值(evaluated)以产生 0 到多个值(value),这些值随后将被插入到结果 List 中。集合元素分为叶元素(leaf elements)和控制流元素(control flow elements)两类。控制流元素即collection if/for,是必须掌握的重要编程技巧,它在Flutter开发中使用的极为广泛。
// ex633.dart
void main() {
print([1 * 1, 2 * 2, 3 * 3]); // 1 Output: [1, 4, 9]
int? a;
print([1, 2, a, 3]); // 2 Output: [1, 2, null, 3]
print([1, 2, ?a, 3]); // 2a Output: [1, 2, 3]
final arr = [3, 4];
var arr2 = [1, 2, ...arr, 5]; // 3
print('${arr2.runtimeType} $arr2'); // Output: List<int> [1, 2, 3, 4, 5]
List<int>? arr3;
print([1, 2, ...?arr3, 3]); // 4 Output: [1, 2, 3]
}
- 此行代码展示的是表达式元素(Expression elements),它属于叶元素;
- 这行中的
?a是 Null-aware 元素,请与上一行代码进行对比; - 这行中的
...arr乃 Spread 元素; - 这行中的
...?arr3乃 Null-aware Spread 元素。
if 元素
if元素 和 for元素 都是控制流元素。下面的示例展示了if元素的各种用法。
// ex634.dart
void main() {
var present = true;
print([0, if (present) 1, 2, 3]); // 1 Output: [0, 1, 2, 3]
print([0, if (!present) 1, 2, 3]); // 1a Output: [0, 2, 3]
var letter = 'A';
print([
if (letter == 'A') 'apple' else 'orange',
'orange',
]); // 2 Output: [apple, orange]
print([
if (letter == 'A') 'apricot' else if (letter == 'O') 'orange',
'orange',
]); // 3 Output: [apricot, orange]
var data = ['apple', 0.2];
print([
if (data case [var name, var price])
'The price of $name is \$$price'
else
'Error data',
]); // 4 Output: [The price of apple is $0.2]
}
for 元素
如下示例简单展示了for元素的用法。
// ex635.dart
void main() {
var numbers = [for (var i = 2; i <= 8; i += 2) i, 10]; // 1
print(numbers); // Output: [2, 4, 6, 8, 10]
var squares = [for (var n in numbers) n * n]; // 2
print(squares); // Output: [4, 16, 36, 64, 100]
}
嵌套控制流元素
控制流元素可以互相嵌套。
// ex636.dart
void main() {
var evens = [
for (var i = 0; i < 10; i++)
if (i % 2 == 0) i,
];
print(evens); // Output: [0, 2, 4, 6, 8]
var numbers = [
for (var i = 0; i < 10; i++)
if (i % 3 == 0)
for (var j = i; j < i + 3; j++)
if (j < 10) j,
];
print(numbers); // Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}