今天我们将学习 Dart 中非常重要的集合类型,这节课先聚焦于 List(列表) 和 Set(集合) 的使用。集合类型可以帮助我们高效地管理和操作多个数据,是开发中不可或缺的工具。
一、List(列表):有序的数据集
List 是 Dart 中最常用的集合类型,它是一个有序的、可重复的数据集合,类似其他语言中的数组。
1. 如何创建 List
创建 List 有多种方式,最常用的有以下几种:
方式一:使用字面量创建(推荐)
void main() {
// 创建整数列表
List<int> numbers = [1, 2, 3, 4, 5];
// 创建字符串列表
List<String> fruits = ['苹果', '香蕉', '橙子'];
// 创建混合类型列表(不推荐,建议指定具体类型)
List<dynamic> mixed = [1, 'hello', true, 3.14];
print(numbers); // 输出:[1, 2, 3, 4, 5]
print(fruits); // 输出:[苹果, 香蕉, 橙子]
}
方式二:使用 List 构造函数
void main() {
// 创建空列表
List<String> emptyList = List.empty();
// 创建固定长度的列表(初始化值为 null)
List<int> fixedList = List.filled(3, 0); // 长度为3,初始值为0
print(fixedList); // 输出:[0, 0, 0]
// 创建可增长的列表
List<String> growableList = List<String>.empty(growable: true);
growableList.add('dart');
print(growableList); // 输出:[dart]
}
2. 访问和修改 List 元素
List 中的元素通过索引访问(索引从 0 开始):
void main() {
List<String> colors = ['红', '绿', '蓝'];
// 访问元素
print(colors[0]); // 输出:红
print(colors[2]); // 输出:蓝
// 修改元素
colors[1] = '黄';
print(colors); // 输出:[红, 黄, 蓝]
// 获取列表长度
print(colors.length); // 输出:3
}
3. 添加和删除元素
对于可增长的列表,我们可以动态添加或删除元素:
void main() {
List<int> nums = [10, 20];
// 添加单个元素
nums.add(30);
print(nums); // 输出:[10, 20, 30]
// 添加多个元素
nums.addAll([40, 50]);
print(nums); // 输出:[10, 20, 30, 40, 50]
// 在指定位置插入元素
nums.insert(1, 15); // 在索引1处插入15
print(nums); // 输出:[10, 15, 20, 30, 40, 50]
// 删除指定值的元素(只删除第一个匹配项)
nums.remove(20);
print(nums); // 输出:[10, 15, 30, 40, 50]
// 删除指定索引的元素
nums.removeAt(0);
print(nums); // 输出:[15, 30, 40, 50]
// 清空列表
nums.clear();
print(nums); // 输出:[]
}
4. 遍历 List
遍历列表是常见操作,最常用的方式是 for-in 循环:
void main() {
List<String> languages = ['Dart', 'Java', 'Python', 'JavaScript'];
// 方式一:for-in 循环(推荐)
for (String lang in languages) {
print(lang);
}
// 方式二:普通 for 循环(需要索引时使用)
for (int i = 0; i < languages.length; i++) {
print('索引 $i: ${languages[i]}');
}
// 方式三:forEach 方法(函数式编程风格)
languages.forEach((lang) {
print('语言:$lang');
});
}
二、Set(集合):无序的去重数据集
Set 是一个无序的、不可重复的数据集合,适合用于存储不希望有重复元素的数据。
1. 创建 Set
void main() {
// 方式一:使用字面量(注意用大括号)
Set<int> numbers = {1, 2, 3, 4};
// 方式二:使用 Set 构造函数
Set<String> fruits = Set.from(['苹果', '香蕉', '苹果']); // 自动去重
print(numbers); // 输出:{1, 2, 3, 4}
print(fruits); // 输出:{苹果, 香蕉}(重复的"苹果"被去除)
// 创建空 Set
Set<String> emptySet1 = {};
Set<String> emptySet2 = Set<String>(); // 构造函数
print(emptySet1.isEmpty); // 输出: true
print(emptySet2.isEmpty); // 输出: true
}
注意:
{}默认创建的是 Set,而不是 Map(后面会学到)。如果要创建空 Map,需要指定类型Map()。
2. Set 的核心特性:去重
当添加重复元素时,Set 会自动忽略:
void main() {
Set<String> names = {'张三', '李四', '张三'};
print(names); // 输出:{张三, 李四}
// 添加重复元素
names.add('李四');
print(names); // 输出:{张三, 李四}(无变化)
}
这个特性非常实用,比如需要对数据进行去重处理时:
void main() {
// 原始列表(有重复元素)
List<int> rawData = [1, 2, 2, 3, 3, 3, 4];
// 转换为 Set 实现去重,再转回 List
List<int> uniqueData = rawData.toSet().toList();
print(uniqueData); // 输出:[1, 2, 3, 4]
}
3. Set 的交集、并集和差集
Set 提供了方便的集合运算方法:
- 并集(union) :两个集合中所有的元素(去重)
- 交集(intersection) :两个集合中共同拥有的元素
- 差集(difference) :当前集合有而另一个集合没有的元素
void main() {
Set<int> a = {1, 2, 3, 4};
Set<int> b = {3, 4, 5, 6};
// 并集:a 和 b 所有元素
Set<int> union = a.union(b);
print(union); // 输出:{1, 2, 3, 4, 5, 6}
// 交集:a 和 b 共有的元素
Set<int> intersection = a.intersection(b);
print(intersection); // 输出:{3, 4}
// 差集:a 有而 b 没有的元素
Set<int> difference = a.difference(b);
print(difference); // 输出:{1, 2}
}
4. Set 的添加和删除操作
Set 的操作与 List 类似,但因为无序,没有索引相关的方法:
void main() {
Set<String> countries = {'中国', '美国'};
// 添加元素
countries.add('日本');
print(countries); // 输出:{中国, 美国, 日本}(顺序可能不同)
// 添加多个元素
countries.addAll(['法国', '德国']);
print(countries); // 输出:{中国, 美国, 日本, 法国, 德国}
// 删除元素
countries.remove('美国');
print(countries); // 输出:{中国, 日本, 法国, 德国}
// 清空集合
countries.clear();
print(countries); // 输出:{}
}
三、集合的常用方法(map/where/toList 等)
Dart 的集合提供了许多强大的方法,让我们可以更简洁地处理数据,这些方法在 List 和 Set 中都可以使用。
1. map ():转换元素
map() 方法用于将集合中的每个元素按照指定规则转换,返回一个新的可迭代对象:
void main() {
List<int> numbers = [1, 2, 3, 4];
// 将每个数字乘以 2
var doubled = numbers.map((n) => n * 2);
// map() 返回的是 Iterable,通常转换为 List
List<int> doubledList = doubled.toList();
print(doubledList); // 输出:[2, 4, 6, 8]
// 字符串处理示例
List<String> names = ['张三', '李四', '王五'];
List<String> greetings = names.map((name) => '你好,$name!').toList();
print(greetings); // 输出:[你好,张三!, 你好,李四!, 你好,王五!]
}
2. where ():筛选元素
where() 方法用于根据条件筛选元素,返回符合条件的元素组成的可迭代对象:
void main() {
List<int> ages = [12, 18, 20, 15, 25, 30];
// 筛选出成年人(年龄 >= 18)
var adults = ages.where((age) => age >= 18);
List<int> adultList = adults.toList();
print(adultList); // 输出:[18, 20, 25, 30]
// Set 筛选示例
Set<double> prices = {19.9, 29.9, 9.9, 39.9, 5.9};
List<double> cheapPrices = prices.where((p) => p < 20).toList();
print(cheapPrices); // 输出:[19.9, 9.9, 5.9]
}
3. toList () 和 toSet ():类型转换
这两个方法用于在 List 和 Set 之间进行转换:
void main() {
Set<String> set = {'a', 'b', 'c'};
List<String> list = set.toList(); // Set 转 List
print(list); // 输出:[a, b, c](顺序可能不同)
List<String> duplicateList = ['a', 'a', 'b'];
Set<String> uniqueSet = duplicateList.toSet(); // List 转 Set(去重)
print(uniqueSet); // 输出:{a, b}
}
4. contains ():检查元素是否存在
void main() {
List<String> fruits = ['苹果', '香蕉', '橙子'];
print(fruits.contains('香蕉')); // 输出:true
print(fruits.contains('西瓜')); // 输出:false
Set<int> nums = {1, 3, 5};
print(nums.contains(3)); // 输出:true
}
5. any () 和 every ():判断条件
any():只要有一个元素满足条件就返回 trueevery():所有元素都满足条件才返回 true
void main() {
List<int> scores = [85, 92, 78, 60];
// 是否有不及格的分数(<60)
bool hasFailed = scores.any((s) => s < 60);
print(hasFailed); // 输出:false(所有都 >=60)
// 是否所有分数都及格
bool allPassed = scores.every((s) => s >= 60);
print(allPassed); // 输出:true
}
四、List 与 Set 的区别与选用场景
| 特性 | List | Set |
|---|---|---|
| 顺序 | 有序(保留插入顺序) | 无序(Dart 3.0+ 部分有序) |
| 重复性 | 允许重复元素 | 不允许重复元素 |
| 索引访问 | 支持(通过索引访问) | 不支持(无索引) |
| 查找效率 | 随元素增多而降低 | 始终较高(哈希表实现) |
选用建议:
- 当需要保持元素顺序或允许重复时,使用 List
- 当需要去重或高效判断元素是否存在时,使用 Set
- 当需要频繁进行集合运算(交集、并集等)时,使用 Set