今天我们将继续探索另外两种重要的数据结构 ——Map(映射) 和 枚举(Enum) ,并深入了解不可变集合的特性与应用。
一、Map(映射):键值对的集合
Map 是一种键值对(key-value) 形式的集合,类似于字典或哈希表。它通过唯一的键(key)来快速访问对应的值(value),是存储关联数据的理想选择。
1. 创建 Map
方式一:使用字面量创建(推荐)
void main() {
// 创建字符串键-整数值的Map
Map<String, int> ages = {'张三': 20, '李四': 22, '王五': 19};
// 创建整数键-字符串值的Map
Map<int, String> weekdays = {1: '星期一', 2: '星期二', 7: '星期日'};
print(ages); // 输出:{张三: 20, 李四: 22, 王五: 19}
print(weekdays); // 输出:{1: 星期一, 2: 星期二, 7: 星期日}
}
方式二:使用 Map 构造函数
void main() {
// 创建空Map
Map<String, String> emptyMap1 = {};
Map<String, String> emptyMap2 = Map<String, String>();
// 从其他可迭代对象创建Map
Map<String, int> numberMap = Map.fromEntries([
MapEntry('one', 1),
MapEntry('two', 2),
]);
print(numberMap); // 输出:{one: 1, two: 2}
}
2. 键值对操作
Map 的核心操作围绕键(key)展开,包括添加、访问、修改和删除:
void main() {
Map<String, String> capitals = {'中国': '北京', '美国': '华盛顿'};
// 访问值(通过键)
print(capitals['中国']); // 输出:北京
// 添加新键值对
capitals['日本'] = '东京';
print(capitals); // 输出:{中国: 北京, 美国: 华盛顿, 日本: 东京}
// 修改值(通过键)
capitals['美国'] = '华盛顿特区';
print(capitals); // 输出:{中国: 北京, 美国: 华盛顿特区, 日本: 东京}
// 删除键值对
capitals.remove('日本');
print(capitals); // 输出:{中国: 北京, 美国: 华盛顿特区}
// 检查是否包含某个键
print(capitals.containsKey('中国')); // 输出:true
// 检查是否包含某个值
print(capitals.containsValue('伦敦')); // 输出:false
// 获取所有键/值
print(capitals.keys); // 输出:(中国, 美国)
print(capitals.values); // 输出:(北京, 华盛顿特区)
// 获取长度
print(capitals.length); // 输出:2
// 清空Map
capitals.clear();
print(capitals); // 输出:{}
}
3. 遍历 Map
遍历 Map 有多种方式,最常用的是 forEach
方法:
void main() {
Map<String, double> prices = {'苹果': 5.99, '香蕉': 3.99, '橙子': 4.50};
// 方式一:forEach遍历(推荐)
prices.forEach((key, value) {
print('$key 的价格是 $value 元');
});
// 输出:
// 苹果 的价格是 5.99 元
// 香蕉 的价格是 3.99 元
// 橙子 的价格是 4.50 元
// 方式二:遍历所有键,再通过键获取值
for (String fruit in prices.keys) {
print('$fruit: ${prices[fruit]}');
}
// 方式三:遍历键值对条目(MapEntry)
for (MapEntry<String, double> entry in prices.entries) {
print('键:${entry.key},值:${entry.value}');
}
}
二、枚举(Enum):有限集合的命名常量
枚举是一种特殊的类型,用于定义固定数量的命名常量,适合表示具有明确可选值的场景(如性别、状态、类型等)。
1. 定义枚举
使用 enum
关键字定义枚举:
// 定义一个表示性别的枚举
enum Gender {
male, // 男性
female, // 女性
other, // 其他
}
// 定义一个表示订单状态的枚举
enum OrderStatus {
pending, // 待支付
paid, // 已支付
shipped, // 已发货
delivered, // 已送达
cancelled, // 已取消
}
2. 使用枚举
枚举的值可以直接访问,且具有类型安全特性:
enum Gender { male, female, other }
void main() {
// 声明枚举变量
Gender userGender = Gender.male;
// 枚举值比较
if (userGender == Gender.male) {
print('用户是男性');
}
// switch-case 中使用枚举(推荐,编译器会检查是否覆盖所有情况)
switch (userGender) {
case Gender.male:
print('性别:男');
break;
case Gender.female:
print('性别:女');
break;
case Gender.other:
print('性别:其他');
break;
}
// 获取枚举的所有值
print(Gender.values); // 输出:[Gender.male, Gender.female, Gender.other]
// 获取枚举值的名称(字符串)
print(userGender.name); // 输出:male
// 通过名称获取枚举值
Gender? gender = Gender.values.firstWhere(
(g) => g.name == 'female',
orElse: () => Gender.other,
);
print(gender); // 输出:Gender.female
}
3. 枚举的使用场景
枚举特别适合以下场景:
- 表示固定的选项集合(如性别、颜色、季节)
- 表示状态机(如订单状态、网络状态)
- 替代魔法数字(提高代码可读性)
反例(不推荐) :
// 用数字表示状态(魔法数字,可读性差)
const int orderPending = 0;
const int orderPaid = 1;
正例(推荐) :
// 用枚举表示状态(清晰易懂)
enum OrderStatus { pending, paid, shipped }
三、不可变集合(const/final 修饰)与性能影响
集合默认是可变的(可以添加 / 删除 / 修改元素),但在很多场景下我们需要不可变集合(创建后不能修改),这时候可以使用 const
或 final
修饰。
1. final 修饰的集合
final
修饰的集合引用不可变(不能重新赋值),但集合本身的内容可以修改:
void main() {
final List<int> numbers = [1, 2, 3];
// 可以修改集合内容
numbers.add(4);
print(numbers); // 输出:[1, 2, 3, 4]
// 不能重新赋值(编译错误)
// numbers = [5, 6, 7];
}
2. const 修饰的集合
const
修饰的集合是完全不可变的(内容和引用都不能修改),且会在编译时创建:
void main() {
// 不可变List
const List<int> immutableList = [1, 2, 3];
// 不可变Set
const Set<String> immutableSet = {'a', 'b'};
// 不可变Map
const Map<String, int> immutableMap = {'one': 1, 'two': 2};
// 尝试修改会报错
// immutableList.add(4); // 编译错误
// immutableSet.remove('a'); // 编译错误
// immutableMap['three'] = 3; // 编译错误
}
3. 不可变集合的性能影响
- 内存优化:相同的
const
集合会共享内存(单例),减少内存占用。
void main() {
const list1 = [1, 2, 3];
const list2 = [1, 2, 3];
print(identical(list1, list2)); // 输出:true(内存地址相同)
}
- 性能提升:
const
集合在编译时初始化,运行时无需再次创建,适合频繁使用的固定数据。 - 线程安全:不可变集合天然线程安全,多线程环境下无需担心并发修改问题。
- 适用场景:配置数据、常量列表、固定选项等不需要修改的数据。
四、集合类型对比与选择指南
集合类型 | 核心特性 | 典型应用场景 |
---|---|---|
List | 有序、可重复、索引访问 | 序列数据(如列表展示、数组计算) |
Set | 无序、不可重复、快速查找 | 去重操作、集合运算(交集 / 并集) |
Map | 键值对、通过键快速访问 | 关联数据(如配置表、字典、缓存) |
Enum | 固定常量集合、类型安全 | 状态表示、选项选择、替代魔法数字 |
选择建议:
- 当需要按顺序存储数据时,用 List
- 当需要确保数据唯一性时,用 Set
- 当需要通过键查找值时,用 Map
- 当需要表示固定选项或状态时,用 Enum