前言
集合 —— 操作批量数据
的核心工具
在 Dart
中,Set
是一种特殊的集合类型,它确保每个元素都是唯一的,不允许重复。就像一个精心整理的工具箱
,每种工具只出现一次
,方便快速查找和使用。Set
适用于需要去重
、检查成员资格
或进行数学集合操作
(如并集
、交集
)的场景。通过 Set
,可以高效地管理和操作不重复的数据项,为应用程序带来简洁性
和高性能
。
操千曲而后晓声,观千剑而后识器。虐它千百遍方能通晓其真意。
一、基本概念
1.1、图像表示
姓名 | 参加的活动 |
---|---|
Alice | 短跑100米 |
Bob | 短跑200米 |
Charlie | 长跑5000米 |
如上图所示,有一个班级的学生名册,确保每个学生的名字只出现一次
,不论他们参加了多少个活动。这就是 Set
的工作方式 —— 它是一个无序且不允许重复元素的集合。
1.2、定义
Set
是一个无序且不允许重复元素的集合。它类似于数学中的集合概念,提供了一种方便的方式来处理唯一
的数据项。
// 创建一个 Set 班级学生名册
Set<String> studentRoster = {'Alice', 'Bob', 'Charlie'};
1.3、特点
- 1、无序:就像名册中的名字,不能保证每次读取的顺序。
- 2、不允许重复:每个学生的名字只能出现
一次
。
// 尝试添加重复的名字
studentRoster.add('Alice'); // 不会添加,因为已经存在
// 输出名册
print(studentRoster); // 输出: {Alice, Bob, Charlie}
1.4、泛型支持
Dart
的 Set
支持泛型,允许指定列表中元素的具体类型
,从而提高代码的类型安全性
和可读性
。
// 创建一个泛型为int类型的Set
Set<int> numbers = {1, 2, 3, 4, 5};
// 创建一个泛型为String类型的list
Set<String> names = {'Alice', 'Bob', 'Charlie'};
// 创建一个泛型为dynamic类型的list
Set<dynamic> list = {'Alice', 1, false};
二、创建和初始化
2.1、直接创建
最简单的方式是直接在花括号{}
中列出元素。
Set<int> numbers = {1, 2, 3, 4, 5};
2.2、使用构造函数
使用 Set
类的构造函数来创建一个空集合
或具有初始元素的集合
。
Dart 代码解读复制代码// 创建一个空集合
Set<String> emptySet = <String>{};
// 创建一个包含初始元素的集合
Set<int> initialNumbers = Set<int>.from([1, 2, 3, 4, 5]);
2.3、 使用 Set.of
构造函数
Set.of
可以从任何可迭代对象(如 List
)创建一个 Set
。
Set<int> fromList = Set.of([1, 2, 3, 4, 5]);
三、访问和修改
3.1、访问元素
由于 Set
本身是无序的,它并不支持通过索引访问元素(如 List
那样)。然而,Dart
提供了多种方式来访问 Set
中的元素,主要包括遍历
和查找特定元素
。下面对应的目录中会有介绍,在此不做过多叙述。
3.2、修改元素
由于 Set
的特性,直接修改某个特定元素并不是像 List
那样简单,因为 Set
不支持通过索引访问和修改元素。可以通过以下几种方法来实现对 Set
中元素的“修改”
。
- 1、移除旧元素并添加新元素:
void main() {
Set<String> colors = {'红色', '蓝色', '绿色'};
// 修改 "红色" 为 "粉红色"
if (colors.remove('红色')) {
colors.add('粉红色');
}
print(colors); // 输出: {蓝色, 绿色, 粉红色}
}
- 2、使用
map
方法创建新的Set
:可以使用map
方法将每个元素映射到新值,然后将结果转换回Set
。
void main() {
Set<int> numbers = {1, 2, 3, 4, 5};
// 将所有数字加 10
Set<int> updatedNumbers = numbers.map((number) => number + 10).toSet();
print(updatedNumbers); // 输出: {11, 12, 13, 14, 15}
}
- 3、批量修改(使用
removeWhere
和addAll
)
void main() {
Set<String> fruits = {'苹果', '香蕉', '橙子'};
// 移除以 "苹" 开头的水果,并添加 "草莓"
fruits.removeWhere((fruit) => fruit.startsWith('苹'));
fruits.add('草莓');
print(fruits); // 输出: {香蕉, 橙子, 草莓}
}
3.3、添加元素
使用 add
或addAll
方法向 Set
中添加新元素。如果元素已经存在,则不会添加。
numbers.add(6);
print(numbers); // 输出: {1, 2, 3, 4, 5, 6
// 使用addAll批量添加元素
numbers.addAll({7, 8, 9});
print(numbers); // 输出: {1, 2, 3, 4, 5, 6, 7, 8, 9}
3.4、移除元素
- 1、移除指定元素:使用
remove
方法。
numbers.remove(5);
print(numbers); // 输出: {1, 2, 3, 4, 6, 7, 8, 9}
- 2、清空集合:使用
clear
方法。
numbers.clear();
print(numbers); // 输出: {}
四、遍历
4.1、使用for
循环
如果确实需要按顺序访问元素,可以先将 Set
转换为 List
,然后通过索引访问。
Set<String> colors = {'红色', '蓝色', '绿色'};
List<String> colorList = colors.toList();
for (int i = 0; i < colorList.length; i++) {
print('颜色 ${i + 1}: ${colorList[i]}');
}
注意:由于 Set
是无序的,转换后的 List
可能不会保持原来的插入顺序。
4.2、使用forEach
forEach
方法允许为 Set
中的每个元素执行一个回调函数。
Set<String> colors = {'红色', '蓝色', '绿色'};
colors.forEach((color) => print(color));
4.3、使用for-in
循环
for-in
循环提供了一种简洁的方式遍历 Set
中的每个元素。
Set<int> numbers = {1, 2, 3, 4, 5};
for (var number in numbers) {
print(number);
}
五、常用属性和方法
5.1、属性
// length:获取 Set 的大小(元素数量)
print(numbers.length); // 输出: 5
// isEmpty 和 isNotEmpty:检查 Set 是否为空。
print(emptySet.isEmpty); // 输出: true
5.2、常用方法
-
1、查找特定元素:
- 使用
contains
方法检查某个元素是否存在于Set
中。
print(numbers.contains(5)); // 输出: true
- 可以结合
where
方法与first
、last
或single
来查找符合条件的第一个或最后一个元素,或者唯一符合条件的元素。
void main() { Set<int> numbers = {1, 2, 3, 4, 5}; // 查找第一个大于 3 的元素 int firstGreaterThanThree = numbers.where((number) => number > 3).first; print('第一个大于 3 的数字是: $firstGreaterThanThree'); // 查找唯一等于 4 的元素 int onlyFour = numbers.singleWhere((number) => number == 4, orElse: () => -1); print('唯一的数字 4 是: $onlyFour'); }
- 使用
any
和every
方法。
void main() { Set<int> numbers = {1, 2, 3, 4, 5}; bool hasEvenNumber = numbers.any((number) => number % 2 == 0); print('Set 中是否有偶数: $hasEvenNumber'); bool allPositive = numbers.every((number) => number > 0); print('所有数字是否都是正数: $allPositive'); }
- 使用
-
2、集合操作:
- 并集:使用
union
方法计算两个Set
的并集。
Set<int> setA = {1, 2, 3}; Set<int> setB = {3, 4, 5}; Set<int> unionSet = setA.union(setB); print(unionSet); // 输出: {1, 2, 3, 4, 5}
- 交集:使用
intersection
方法计算两个Set
的交集。
Set<int> intersectionSet = setA.intersection(setB); print(intersectionSet); // 输出: {3}
- 差集:使用
difference
方法计算两个Set
的差集。
Set<int> differenceSet = setA.difference(setB); print(differenceSet); // 输出: {1, 2}
- 并集:使用
六、不可变Set
(Set.unmodifiable
)
有时需要确保一个 Set
不会被修改。可以使用 Set.unmodifiable
来创建一个不可变的 Set
。
Set<int> immutableNumbers = Set.unmodifiable({1, 2, 3, 4, 5});
// 下面这行代码会抛出异常,因为 immutableNumbers 是不可变的
// immutableNumbers.add(6);
七、总结
Set
提供了一种强大而灵活的方式来处理唯一
的数据项。通过合理利用 Set
的特性,可以编写出更加简洁
、高效和易于维护
的代码。