Dart Map API 详细总结
基于Dart官方API文档与实践经验整理
目录
概述
在Dart中,Map
是一个键值对的集合,类似于其他语言中的字典或哈希表。Map具有以下特性:
- 键值对结构: 每个元素包含一个键和一个值
- 键的唯一性: 每个键在Map中只能出现一次
- 高效查找: 基于哈希表实现,查找效率高
- 泛型支持: 支持类型安全的泛型操作
- 动态大小: 可以动态增长和收缩
创建Map
1. 字面量语法创建
dart
// 创建包含初始值的Map
Map<String, int> scores = {
'Alice': 95,
'Bob': 87,
'Charlie': 92
};
Map<int, String> names = {
1: 'First',
2: 'Second',
3: 'Third'
};
// 创建空Map
Map<String, int> emptyScores = <String, int>{};
Map<int, String> emptyNames = <int, String>{};
// 类型推断
var fruits = {'apple': 5, 'banana': 3, 'orange': 8}; // Map<String, int>
var colors = {1: 'red', 2: 'green', 3: 'blue'}; // Map<int, String>
// 注意:空大括号{}默认创建Map<dynamic, dynamic>
var empty = {}; // Map<dynamic, dynamic>
var typedEmpty = <String, int>{}; // Map<String, int>
2. 构造函数创建
dart
// 创建空Map
Map<String, int> emptyMap = Map<String, int>();
// 参数: 无
// 返回: Map<K, V> - 新的空Map
// 从其他Map创建
Map<String, int> originalMap = {'a': 1, 'b': 2};
Map<String, int> fromMap = Map<String, int>.from(originalMap);
// 参数: other (Map) - 源Map
// 返回: Map<K, V> - 新的Map副本
Map<String, int> ofMap = Map<String, int>.of(originalMap);
// 参数: other (Map<K, V>) - 源Map(类型安全)
// 返回: Map<K, V> - 新的Map副本
// 从键值对列表创建
Map<String, int> fromEntries = Map.fromEntries([
MapEntry('x', 10),
MapEntry('y', 20),
MapEntry('z', 30)
]);
// 参数: entries (Iterable<MapEntry<K, V>>) - 键值对可迭代对象
// 返回: Map<K, V> - 新的Map
// 从可迭代对象创建
List<String> keys = ['a', 'b', 'c'];
Map<String, int> fromIterables = Map.fromIterables(
keys,
[1, 2, 3]
);
// 参数:
// keys (Iterable<K>) - 键的可迭代对象
// values (Iterable<V>) - 值的可迭代对象
// 返回: Map<K, V> - 新的Map
// 注意: 两个可迭代对象长度必须相等
// 创建不可变Map
Map<String, int> unmodifiableMap = Map.unmodifiable({'a': 1, 'b': 2});
// 参数: other (Map<K, V>) - 源Map
// 返回: Map<K, V> - 不可修改的Map
// 创建身份Map(基于对象身份而非相等性)
Map<String, int> identityMap = Map.identity();
// 参数: 无
// 返回: Map<K, V> - 基于身份比较的Map
构造函数使用注意事项
⚠️ 重要提醒:
-
Map.from vs Map.of的区别:
dartdynamic dynamicMap = {'a': 1, 'b': 2}; // Map.from 接受任意Map,可能有运行时类型错误 Map<String, int> fromMap = Map<String, int>.from(dynamicMap); // Map.of 要求类型匹配,编译时类型安全 // Map<String, int> ofMap = Map<String, int>.of(dynamicMap); // 编译错误 Map<String, int> ofMap = Map<String, int>.of(<String, int>{'a': 1, 'b': 2}); // 正确
-
fromIterables的长度匹配:
dartList<String> keys = ['a', 'b', 'c']; List<int> values = [1, 2]; // 长度不匹配 // ❌ 运行时会抛出异常 // Map<String, int> map = Map.fromIterables(keys, values); // StateError // ✅ 确保长度匹配 if (keys.length == values.length) { Map<String, int> map = Map.fromIterables(keys, values); }
-
身份Map vs 相等性Map:
dartMap<String, int> normalMap = {'hello': 1}; Map<String, int> identityMap = Map.identity()..['hello'] = 1; String hello1 = 'hello'; String hello2 = 'hello'; print(normalMap[hello1]); // 1 print(normalMap[hello2]); // 1 - 基于值相等 identityMap.clear(); identityMap[hello1] = 1; print(identityMap[hello1]); // 1 print(identityMap[hello2]); // 1 - 字符串字面量共享引用 // 对于对象更明显 class Person { String name; Person(this.name); } Person p1 = Person('Alice'); Person p2 = Person('Alice'); Map<Person, int> normalPersonMap = {p1: 1, p2: 2}; // 可能包含两个条目 Map<Person, int> identityPersonMap = Map.identity(); identityPersonMap[p1] = 1; identityPersonMap[p2] = 2; // 一定包含两个条目
基本属性
dart
Map<String, int> scores = {'Alice': 95, 'Bob': 87, 'Charlie': 92};
// 长度
print(scores.length); // 3
// 是否为空
print(scores.isEmpty); // false
print(scores.isNotEmpty); // true
// 获取所有键
Iterable<String> keys = scores.keys;
print(keys); // (Alice, Bob, Charlie)
// 获取所有值
Iterable<int> values = scores.values;
print(values); // (95, 87, 92)
// 获取所有键值对
Iterable<MapEntry<String, int>> entries = scores.entries;
for (MapEntry<String, int> entry in entries) {
print('${entry.key}: ${entry.value}');
}
// 检查是否包含键或值
print(scores.containsKey('Alice')); // true
print(scores.containsValue(95)); // true
基本属性使用注意事项
⚠️ 重要提醒:
-
keys和values的顺序:
dartMap<String, int> map = {'c': 3, 'a': 1, 'b': 2}; // ❌ 不要假设keys和values有特定顺序(HashMap是无序的) List<String> keyList = map.keys.toList(); List<int> valueList = map.values.toList(); // keyList和valueList的顺序可能不是插入顺序 // ✅ 如果需要有序,使用LinkedHashMap import 'dart:collection'; LinkedHashMap<String, int> orderedMap = LinkedHashMap.from(map);
-
entries的使用优势:
dartMap<String, int> scores = {'Alice': 95, 'Bob': 87}; // ❌ 效率较低的遍历方式 for (String key in scores.keys) { int value = scores[key]!; // 需要二次查找 print('$key: $value'); } // ✅ 高效的遍历方式 for (MapEntry<String, int> entry in scores.entries) { print('${entry.key}: ${entry.value}'); // 只需一次查找 }
访问和修改元素
dart
Map<String, int> scores = {'Alice': 95, 'Bob': 87, 'Charlie': 92};
// 通过键访问值
print(scores['Alice']); // 95
print(scores['David']); // null (键不存在)
// 参数: key (Object?) - 要访问的键
// 返回: V? - 对应的值,如果键不存在返回null
// 修改值
scores['Alice'] = 98;
print(scores['Alice']); // 98
// 参数: key (K) - 要修改的键,value (V) - 新值
// 返回: void
// 添加新键值对
scores['David'] = 88;
print(scores); // {Alice: 98, Bob: 87, Charlie: 92, David: 88}
// 使用putIfAbsent添加(仅当键不存在时)
int eveScore = scores.putIfAbsent('Eve', () => 90);
// 参数:
// key (K) - 要添加的键
// ifAbsent (V Function()) - 当键不存在时调用的函数
// 返回: V - 键对应的值(现有的或新添加的)
print(eveScore); // 90
print(scores['Eve']); // 90
// 再次调用putIfAbsent,键已存在
int eveScore2 = scores.putIfAbsent('Eve', () => 95);
print(eveScore2); // 90 (返回现有值,不调用函数)
// 使用update修改值
scores.update('Alice', (value) => value + 2);
// 参数:
// key (K) - 要更新的键
// update (V Function(V)) - 更新函数,接收当前值返回新值
// ifAbsent (V Function()?, 可选) - 键不存在时的处理函数
// 返回: V - 更新后的值
print(scores['Alice']); // 100
// 带默认值的update
scores.update('Frank', (value) => value + 10, ifAbsent: () => 85);
print(scores['Frank']); // 85
// 使用updateAll批量更新
scores.updateAll((key, value) => value + 5);
// 参数: update (V Function(K, V)) - 更新函数,接收键和值返回新值
// 返回: void
print(scores); // 所有分数都增加了5
访问和修改使用注意事项
⚠️ 重要提醒:
-
空安全访问:
dartMap<String, String?> nullableMap = {'key1': 'value1', 'key2': null}; // ❌ 直接访问可能得到null String? value = nullableMap['key1']; // 可能为null // ✅ 提供默认值 String safeValue = nullableMap['key1'] ?? 'default'; // ✅ 使用containsKey检查 if (nullableMap.containsKey('key1')) { String? definiteValue = nullableMap['key1']; // 这里definiteValue仍可能为null,因为值本身可能是null } // ✅ 区分键不存在和值为null if (nullableMap.containsKey('missing')) { print('键存在,值为: ${nullableMap['missing']}'); } else { print('键不存在'); }
-
putIfAbsent的性能考虑:
dartMap<String, List<int>> groupedData = {}; // ❌ 每次都创建新List(即使键已存在) groupedData.putIfAbsent('key', () => <int>[]).add(1); // ✅ 更高效的方式 (groupedData['key'] ??= <int>[]).add(1); // ✅ 或使用containsKey检查 if (!groupedData.containsKey('key')) { groupedData['key'] = <int>[]; } groupedData['key']!.add(1);
-
update方法的异常处理:
dartMap<String, int> scores = {'Alice': 95}; // ❌ 键不存在时会抛出ArgumentError try { scores.update('Bob', (value) => value + 10); } catch (e) { print('更新失败: $e'); } // ✅ 提供ifAbsent处理 scores.update('Bob', (value) => value + 10, ifAbsent: () => 80);
添加和删除元素
dart
Map<String, int> scores = {'Alice': 95, 'Bob': 87};
// 添加单个键值对
scores['Charlie'] = 92;
print(scores); // {Alice: 95, Bob: 87, Charlie: 92}
// 添加多个键值对
scores.addAll({'David': 88, 'Eve': 91});
// 参数: other (Map<K, V>) - 要添加的Map
// 返回: void
print(scores); // {Alice: 95, Bob: 87, Charlie: 92, David: 88, Eve: 91}
// 从键值对列表添加
scores.addEntries([
MapEntry('Frank', 89),
MapEntry('Grace', 94)
]);
// 参数: newEntries (Iterable<MapEntry<K, V>>) - 要添加的键值对
// 返回: void
print(scores.length); // 7
// 删除单个键值对
int? removedScore = scores.remove('Bob');
// 参数: key (Object?) - 要删除的键
// 返回: V? - 被删除的值,如果键不存在返回null
print(removedScore); // 87
print(scores.containsKey('Bob')); // false
// 根据条件删除
scores.removeWhere((key, value) => value < 90);
// 参数: test (bool Function(K, V)) - 测试函数,返回true的键值对将被删除
// 返回: void
print(scores); // 只保留分数>=90的学生
// 清空Map
scores.clear();
// 参数: 无
// 返回: void
print(scores.isEmpty); // true
添加删除使用注意事项
⚠️ 重要提醒:
-
addAll的覆盖行为:
dartMap<String, int> original = {'a': 1, 'b': 2}; Map<String, int> toAdd = {'b': 20, 'c': 3}; original.addAll(toAdd); print(original); // {a: 1, b: 20, c: 3} - 'b'的值被覆盖
-
remove返回值的利用:
dartMap<String, List<String>> cache = { 'key1': ['value1', 'value2'] }; // ✅ 利用返回值进行清理 List<String>? removed = cache.remove('key1'); removed?.clear(); // 清理被删除的List
-
不可变Map的操作限制:
dartMap<String, int> immutableMap = Map.unmodifiable({'a': 1, 'b': 2}); // ❌ 这些操作都会抛出UnsupportedError // immutableMap['c'] = 3; // immutableMap.addAll({'d': 4}); // immutableMap.remove('a'); // immutableMap.clear();
查找和检查
dart
Map<String, int> scores = {
'Alice': 95,
'Bob': 87,
'Charlie': 92,
'David': 88
};
// 检查是否包含键
print(scores.containsKey('Alice')); // true
print(scores.containsKey('Eve')); // false
// 参数: key (Object?) - 要检查的键
// 返回: bool - 如果包含该键返回true,否则返回false
// 检查是否包含值
print(scores.containsValue(95)); // true
print(scores.containsValue(100)); // false
// 参数: value (Object?) - 要检查的值
// 返回: bool - 如果包含该值返回true,否则返回false
// 遍历Map
print('=== forEach遍历 ===');
scores.forEach((key, value) {
print('$key: $value');
});
// 参数: f (void Function(K, V)) - 遍历函数,接收键和值
// 返回: void
print('=== entries遍历 ===');
for (MapEntry<String, int> entry in scores.entries) {
print('${entry.key}: ${entry.value}');
}
print('=== keys遍历 ===');
for (String key in scores.keys) {
print('$key: ${scores[key]}');
}
// 查找满足条件的键值对
MapEntry<String, int>? highScorer = scores.entries
.where((entry) => entry.value > 90)
.firstOrNull;
if (highScorer != null) {
print('高分学生: ${highScorer.key} (${highScorer.value})');
}
// 检查是否所有值都满足条件
bool allPassed = scores.values.every((score) => score >= 60);
print('所有人都及格: $allPassed');
// 检查是否有值满足条件
bool hasExcellent = scores.values.any((score) => score >= 95);
print('有优秀成绩: $hasExcellent');
查找检查使用注意事项
⚠️ 重要提醒:
-
containsKey vs 直接访问的性能:
dartMap<String, int> largeMap = Map.fromIterables( List.generate(10000, (i) => 'key$i'), List.generate(10000, (i) => i) ); // ✅ 如果需要值,直接访问更高效 int? value = largeMap['key5000']; if (value != null) { print('找到值: $value'); } // ❌ 不必要的双重查找 if (largeMap.containsKey('key5000')) { int value = largeMap['key5000']!; // 第二次查找 print('找到值: $value'); }
-
forEach vs for-in性能:
dartMap<String, int> scores = {'Alice': 95, 'Bob': 87, 'Charlie': 92}; // ✅ forEach通常更高效 scores.forEach((key, value) { print('$key: $value'); }); // ✅ entries遍历也很高效 for (MapEntry<String, int> entry in scores.entries) { print('${entry.key}: ${entry.value}'); } // ❌ keys遍历需要额外查找 for (String key in scores.keys) { int value = scores[key]!; // 额外的查找操作 print('$key: $value'); }
转换和映射
dart
Map<String, int> scores = {
'Alice': 95,
'Bob': 87,
'Charlie': 92,
'David': 88
};
// map: 转换键值对
Map<String, String> letterGrades = scores.map((key, value) {
String grade = value >= 90 ? 'A' : value >= 80 ? 'B' : 'C';
return MapEntry(key, grade);
});
// 参数: f (MapEntry<K2, V2> Function(K, V)) - 转换函数,接收键值返回新的MapEntry
// 返回: Map<K2, V2> - 转换后的Map
print(letterGrades); // {Alice: A, Bob: B, Charlie: A, David: B}
// 只转换值
Map<String, double> percentages = scores.map((key, value) {
return MapEntry(key, value / 100.0);
});
print(percentages); // {Alice: 0.95, Bob: 0.87, Charlie: 0.92, David: 0.88}
// 只转换键
Map<int, int> indexedScores = Map.fromEntries(
scores.entries.map((entry) => MapEntry(
entry.key.hashCode,
entry.value
))
);
// 过滤Map
Map<String, int> highScores = Map.fromEntries(
scores.entries.where((entry) => entry.value >= 90)
);
print(highScores); // {Alice: 95, Charlie: 92}
// 转换为其他集合类型
List<String> names = scores.keys.toList();
List<int> scoresList = scores.values.toList();
Set<int> uniqueScores = scores.values.toSet();
// 转换为字符串
String scoresString = scores.entries
.map((entry) => '${entry.key}: ${entry.value}')
.join(', ');
print(scoresString); // Alice: 95, Bob: 87, Charlie: 92, David: 88
// fold: 累积操作
int totalScore = scores.values.fold(0, (sum, score) => sum + score);
double averageScore = totalScore / scores.length;
print('平均分: ${averageScore.toStringAsFixed(1)}');
// 分组操作
Map<String, List<String>> gradeGroups = {};
scores.forEach((name, score) {
String grade = score >= 90 ? 'A' : score >= 80 ? 'B' : 'C';
gradeGroups.putIfAbsent(grade, () => []).add(name);
});
print(gradeGroups); // {A: [Alice, Charlie], B: [Bob, David]}
转换映射使用注意事项
⚠️ 重要提醒:
-
map操作的键冲突处理:
dartMap<String, int> original = {'Alice': 95, 'alice': 87}; // ❌ 转换可能产生键冲突 Map<String, int> upperCased = original.map((key, value) { return MapEntry(key.toUpperCase(), value); }); print(upperCased); // {ALICE: 87} - 后面的值覆盖前面的 // ✅ 处理键冲突 Map<String, List<int>> grouped = {}; original.forEach((key, value) { String upperKey = key.toUpperCase(); grouped.putIfAbsent(upperKey, () => []).add(value); }); print(grouped); // {ALICE: [95, 87]}
-
大Map的转换性能:
dartMap<String, int> largeMap = Map.fromIterables( List.generate(100000, (i) => 'key$i'), List.generate(100000, (i) => i) ); // ❌ 一次性转换可能消耗大量内存 Map<String, String> converted = largeMap.map((k, v) => MapEntry(k, v.toString())); // ✅ 按需转换或分批处理 String getValue(String key) { int? intValue = largeMap[key]; return intValue?.toString() ?? 'not found'; }
-
转换中的异常处理:
dartMap<String, String> stringNumbers = {'a': '123', 'b': 'abc', 'c': '456'}; // ❌ 可能抛出异常 // Map<String, int> integers = stringNumbers.map((k, v) => MapEntry(k, int.parse(v))); // ✅ 安全转换 Map<String, int> safeIntegers = {}; stringNumbers.forEach((key, value) { int? parsed = int.tryParse(value); if (parsed != null) { safeIntegers[key] = parsed; } }); print(safeIntegers); // {a: 123, c: 456}
高级操作
1. 合并和组合Map
dart
Map<String, int> map1 = {'a': 1, 'b': 2};
Map<String, int> map2 = {'b': 20, 'c': 3};
Map<String, int> map3 = {'c': 30, 'd': 4};
// 简单合并(后面的覆盖前面的)
Map<String, int> merged = {...map1, ...map2, ...map3};
print(merged); // {a: 1, b: 20, c: 30, d: 4}
// 自定义合并逻辑
Map<String, int> customMerged = Map.from(map1);
map2.forEach((key, value) {
customMerged.update(key, (existing) => existing + value, ifAbsent: () => value);
});
print(customMerged); // {a: 1, b: 22, c: 3} - 'b'的值是2+20=22
// 合并多个Map
List<Map<String, int>> maps = [map1, map2, map3];
Map<String, int> combinedSum = {};
for (Map<String, int> map in maps) {
map.forEach((key, value) {
combinedSum.update(key, (existing) => existing + value, ifAbsent: () => value);
});
}
print(combinedSum); // {a: 1, b: 22, c: 33, d: 4}
2. Map的深拷贝和浅拷贝
dart
// 浅拷贝
Map<String, List<int>> original = {
'list1': [1, 2, 3],
'list2': [4, 5, 6]
};
Map<String, List<int>> shallowCopy = Map.from(original);
shallowCopy['list1'].add(999); // 修改会影响原Map
print(original['list1']); // [1, 2, 3, 999] - 被影响了
// 深拷贝
Map<String, List<int>> deepCopy = original.map((key, value) {
return MapEntry(key, List.from(value)); // 复制List
});
deepCopy['list2'].add(888); // 不会影响原Map
print(original['list2']); // [4, 5, 6] - 未被影响
// 复杂对象的深拷贝
class Person {
String name;
int age;
Person(this.name, this.age);
// 拷贝构造函数
Person.copy(Person other) : name = other.name, age = other.age;
@override
String toString() => 'Person($name, $age)';
}
Map<String, Person> people = {
'p1': Person('Alice', 25),
'p2': Person('Bob', 30)
};
Map<String, Person> peopleCopy = people.map((key, person) {
return MapEntry(key, Person.copy(person));
});
3. Map的分组和聚合
dart
// 学生成绩数据
List<Map<String, dynamic>> students = [
{'name': 'Alice', 'grade': 'A', 'score': 95, 'subject': 'Math'},
{'name': 'Bob', 'grade': 'B', 'score': 87, 'subject': 'Math'},
{'name': 'Alice', 'grade': 'A', 'score': 92, 'subject': 'English'},
{'name': 'Charlie', 'grade': 'A', 'score': 88, 'subject': 'Math'},
{'name': 'Bob', 'grade': 'B', 'score': 85, 'subject': 'English'},
];
// 按年级分组
Map<String, List<Map<String, dynamic>>> groupedByGrade = {};
for (var student in students) {
String grade = student['grade'];
groupedByGrade.putIfAbsent(grade, () => []).add(student);
}
print('按年级分组: $groupedByGrade');
// 按学生姓名聚合分数
Map<String, List<int>> scoresByStudent = {};
for (var student in students) {
String name = student['name'];
int score = student['score'];
scoresByStudent.putIfAbsent(name, () => []).add(score);
}
// 计算平均分
Map<String, double> averageScores = scoresByStudent.map((name, scores) {
double average = scores.reduce((a, b) => a + b) / scores.length;
return MapEntry(name, average);
});
print('平均分: $averageScores');
// 统计各科目的最高分
Map<String, int> maxScoresBySubject = {};
for (var student in students) {
String subject = student['subject'];
int score = student['score'];
maxScoresBySubject.update(
subject,
(existing) => existing > score ? existing : score,
ifAbsent: () => score
);
}
print('各科最高分: $maxScoresBySubject');
4. Map的索引和反转
dart
// 创建索引Map
List<String> items = ['apple', 'banana', 'cherry', 'date'];
Map<int, String> indexMap = {
for (int i = 0; i < items.length; i++) i: items[i]
};
print(indexMap); // {0: apple, 1: banana, 2: cherry, 3: date}
// 反转Map(键值互换)
Map<String, int> original = {'a': 1, 'b': 2, 'c': 3};
Map<int, String> reversed = {
for (var entry in original.entries) entry.value: entry.key
};
print(reversed); // {1: a, 2: b, 3: c}
// 处理值不唯一的情况
Map<String, String> colors = {
'apple': 'red',
'banana': 'yellow',
'cherry': 'red',
'lemon': 'yellow'
};
// 反转时处理重复值
Map<String, List<String>> reversedColors = {};
colors.forEach((fruit, color) {
reversedColors.putIfAbsent(color, () => []).add(fruit);
});
print(reversedColors); // {red: [apple, cherry], yellow: [banana, lemon]}
5. 缓存和备忘录模式
dart
// 简单缓存实现
class SimpleCache<K, V> {
final Map<K, V> _cache = {};
final int maxSize;
SimpleCache({this.maxSize = 100});
V? get(K key) => _cache[key];
void put(K key, V value) {
if (_cache.length >= maxSize) {
// 简单的清理策略:删除第一个元素
_cache.remove(_cache.keys.first);
}
_cache[key] = value;
}
bool containsKey(K key) => _cache.containsKey(key);
void clear() => _cache.clear();
int get size => _cache.length;
}
// 使用示例
SimpleCache<String, int> cache = SimpleCache(maxSize: 3);
cache.put('a', 1);
cache.put('b', 2);
cache.put('c', 3);
cache.put('d', 4); // 会删除'a'
print(cache.get('a')); // null
print(cache.get('d')); // 4
// 备忘录模式实现
class Fibonacci {
static final Map<int, int> _memo = {};
static int calculate(int n) {
if (n <= 1) return n;
return _memo.putIfAbsent(n, () {
return calculate(n - 1) + calculate(n - 2);
});
}
static void clearCache() => _memo.clear();
static int get cacheSize => _memo.length;
}
// 测试备忘录
print(Fibonacci.calculate(10)); // 55
print('缓存大小: ${Fibonacci.cacheSize}'); // 9
6. 多级Map和嵌套操作
dart
// 多级Map结构
Map<String, Map<String, int>> nestedScores = {
'Math': {'Alice': 95, 'Bob': 87, 'Charlie': 92},
'English': {'Alice': 88, 'Bob': 91, 'Charlie': 85},
'Science': {'Alice': 92, 'Bob': 89, 'Charlie': 94}
};
// 安全访问嵌套值
int? getScore(String subject, String student) {
return nestedScores[subject]?[student];
}
print(getScore('Math', 'Alice')); // 95
print(getScore('History', 'Alice')); // null
// 设置嵌套值
void setScore(String subject, String student, int score) {
nestedScores.putIfAbsent(subject, () => {})[student] = score;
}
setScore('History', 'Alice', 90);
print(nestedScores['History']); // {Alice: 90}
// 扁平化嵌套Map
Map<String, int> flattenScores() {
Map<String, int> flattened = {};
nestedScores.forEach((subject, studentScores) {
studentScores.forEach((student, score) {
flattened['${subject}_$student'] = score;
});
});
return flattened;
}
print(flattenScores()); // {Math_Alice: 95, Math_Bob: 87, ...}
// 计算每个学生的总分
Map<String, int> calculateTotalScores() {
Map<String, int> totals = {};
nestedScores.forEach((subject, studentScores) {
studentScores.forEach((student, score) {
totals.update(student, (existing) => existing + score, ifAbsent: () => score);
});
});
return totals;
}
print(calculateTotalScores()); // {Alice: 275, Bob: 267, Charlie: 271}
方法参数速查表
构造方法
方法 | 参数 | 说明 | 返回值 |
---|---|---|---|
Map() |
无 | 创建空Map | Map<K, V> |
Map.from() |
other: Map |
从其他Map创建 | Map<K, V> |
Map.of() |
other: Map<K, V> |
从其他Map创建(类型安全) | Map<K, V> |
Map.unmodifiable() |
other: Map<K, V> |
创建不可修改Map | Map<K, V> |
Map.identity() |
无 | 创建基于身份比较的Map | Map<K, V> |
Map.fromEntries() |
entries: Iterable<MapEntry<K, V>> |
从键值对创建 | Map<K, V> |
Map.fromIterables() |
keys: Iterable<K> values: Iterable<V> |
从两个可迭代对象创建 | Map<K, V> |
访问和修改方法
方法 | 参数 | 说明 | 返回值 |
---|---|---|---|
[] |
key: Object? |
获取键对应的值 | V? |
[]= |
key: K value: V |
设置键值对 | void |
putIfAbsent() |
key: K ifAbsent: V Function() |
键不存在时添加 | V |
update() |
key: K update: V Function(V) ifAbsent: V Function()? |
更新键对应的值 | V |
updateAll() |
update: V Function(K, V) |
更新所有键值对 | void |
添加删除方法
方法 | 参数 | 说明 | 返回值 |
---|---|---|---|
addAll() |
other: Map<K, V> |
添加其他Map的所有键值对 | void |
addEntries() |
newEntries: Iterable<MapEntry<K, V>> |
添加键值对列表 | void |
remove() |
key: Object? |
删除键值对 | V? |
removeWhere() |
test: bool Function(K, V) |
根据条件删除 | void |
clear() |
无 | 清空Map | void |
查询方法
方法 | 参数 | 说明 | 返回值 |
---|---|---|---|
containsKey() |
key: Object? |
检查是否包含键 | bool |
containsValue() |
value: Object? |
检查是否包含值 | bool |
forEach() |
f: void Function(K, V) |
遍历所有键值对 | void |
转换方法
方法 | 参数 | 说明 | 返回值 |
---|---|---|---|
map() |
f: MapEntry<K2, V2> Function(K, V) |
转换键值对 | Map<K2, V2> |
cast() |
无(泛型参数) | 类型转换 | Map<RK, RV> |
性能考虑
时间复杂度
操作 | HashMap | LinkedHashMap | SplayTreeMap | 说明 |
---|---|---|---|---|
[] (访问) |
O(1)* | O(1)* | O(log n) | *摊销时间,最坏O(n) |
[]= (设置) |
O(1)* | O(1)* | O(log n) | *摊销时间,最坏O(n) |
containsKey() |
O(1)* | O(1)* | O(log n) | 查找键 |
containsValue() |
O(n) | O(n) | O(n) | 需要遍历所有值 |
remove() |
O(1)* | O(1)* | O(log n) | 删除操作 |
forEach() |
O(n) | O(n) | O(n) | 遍历所有元素 |
排序输出 | O(n log n) | O(n log n) | O(n) | TreeMap已排序 |
*摊销时间复杂度,哈希冲突时可能退化
空间复杂度
Map类型 | 空间复杂度 | 额外开销 | 说明 |
---|---|---|---|
HashMap | O(n) | ~25-50% | 哈希表空间,负载因子影响 |
LinkedHashMap | O(n) | ~50-75% | 额外的链表指针 |
SplayTreeMap | O(n) | ~100% | 树节点指针开销 |
性能优化建议
dart
// 1. 选择合适的Map实现
import 'dart:collection';
// 需要最快的访问速度
Map<String, int> fastMap = HashMap<String, int>();
// 需要保持插入顺序(默认实现)
Map<String, int> orderedMap = <String, int>{};
// 需要自动排序
SplayTreeMap<String, int> sortedMap = SplayTreeMap<String, int>();
// 2. 预估大小以优化性能
Map<String, int> optimizedMap = HashMap<String, int>();
// 在Dart中,HashMap会自动调整大小
// 3. 批量操作优化
Map<String, int> target = {};
Map<String, int> source = {'a': 1, 'b': 2, 'c': 3};
// ✅ 使用addAll而不是循环添加
target.addAll(source);
// ❌ 效率较低
// source.forEach((key, value) => target[key] = value);
// 4. 避免频繁的containsKey检查
Map<String, List<String>> groups = {};
// ❌ 效率较低
String key = 'group1';
if (!groups.containsKey(key)) {
groups[key] = [];
}
groups[key]!.add('item');
// ✅ 使用putIfAbsent
(groups.putIfAbsent(key, () => [])).add('item');
// ✅ 或使用null-aware操作符
(groups[key] ??= []).add('item');
常见陷阱和错误
1. 键的相等性和哈希问题
dart
class BadPerson {
String name;
int age;
BadPerson(this.name, this.age);
// ❌ 只重写了==,没有重写hashCode
@override
bool operator ==(Object other) =>
other is BadPerson && other.name == name;
@override
String toString() => 'BadPerson($name, $age)';
}
class GoodPerson {
String name;
int age;
GoodPerson(this.name, this.age);
// ✅ 同时重写==和hashCode
@override
bool operator ==(Object other) =>
other is GoodPerson && other.name == name && other.age == age;
@override
int get hashCode => Object.hash(name, age);
@override
String toString() => 'GoodPerson($name, $age)';
}
// 测试
Map<BadPerson, String> badMap = {};
badMap[BadPerson('Alice', 25)] = 'first';
badMap[BadPerson('Alice', 25)] = 'second';
print('Bad map length: ${badMap.length}'); // 可能是2(错误)
Map<GoodPerson, String> goodMap = {};
goodMap[GoodPerson('Alice', 25)] = 'first';
goodMap[GoodPerson('Alice', 25)] = 'second';
print('Good map length: ${goodMap.length}'); // 1(正确)
2. 修改不可变Map
dart
Map<String, int> immutableMap = Map.unmodifiable({'a': 1, 'b': 2});
// ❌ 试图修改不可变Map
try {
immutableMap['c'] = 3; // UnsupportedError
immutableMap.remove('a'); // UnsupportedError
immutableMap.clear(); // UnsupportedError
} catch (e) {
print('不可变Map错误: $e');
}
// ✅ 检查Map是否可修改
extension MapExtensions<K, V> on Map<K, V> {
bool get isModifiable {
try {
var testKey = keys.isNotEmpty ? keys.first : null;
if (testKey != null) {
var originalValue = this[testKey];
this[testKey] = originalValue;
return true;
}
// 如果Map为空,尝试添加一个测试键值对
return false; // 简化处理
} catch (e) {
return false;
}
}
}
3. 并发修改异常
dart
Map<String, int> scores = {'Alice': 95, 'Bob': 87, 'Charlie': 92};
// ❌ 在遍历时修改Map会抛出异常
try {
scores.forEach((key, value) {
if (value < 90) {
scores.remove(key); // ConcurrentModificationError!
}
});
} catch (e) {
print('并发修改错误: $e');
}
// ✅ 正确的方式:使用removeWhere
scores.removeWhere((key, value) => value < 90);
// ✅ 或者先收集要删除的键
List<String> toRemove = [];
scores.forEach((key, value) {
if (value < 90) {
toRemove.add(key);
}
});
toRemove.forEach(scores.remove);
4. 空值处理混淆
dart
Map<String, String?> nullableMap = {
'key1': 'value1',
'key2': null,
};
// 区分键不存在和值为null
print(nullableMap['key1']); // 'value1'
print(nullableMap['key2']); // null (值为null)
print(nullableMap['key3']); // null (键不存在)
print(nullableMap.containsKey('key2')); // true (键存在,值为null)
print(nullableMap.containsKey('key3')); // false (键不存在)
// ❌ 错误的判断方式
if (nullableMap['key2'] != null) {
print('key2有值'); // 不会执行,因为值为null
}
// ✅ 正确的判断方式
if (nullableMap.containsKey('key2')) {
print('key2存在'); // 会执行
String? value = nullableMap['key2'];
if (value != null) {
print('key2的值是: $value');
} else {
print('key2的值为null');
}
}
5. 类型转换错误
dart
Map<dynamic, dynamic> dynamicMap = {
'string_key': 'string_value',
123: 456,
'list_key': [1, 2, 3]
};
// ❌ 不安全的类型转换
try {
Map<String, String> stringMap = dynamicMap.cast<String, String>();
print(stringMap[123]); // 运行时错误!键不是String
} catch (e) {
print('类型转换错误: $e');
}
// ✅ 安全的类型转换
Map<String, String> safeStringMap = {};
dynamicMap.forEach((key, value) {
if (key is String && value is String) {
safeStringMap[key] = value;
}
});
print(safeStringMap); // {string_key: string_value}
// ✅ 使用whereType进行过滤(对于entries)
Map<String, String> filteredMap = Map.fromEntries(
dynamicMap.entries
.where((entry) => entry.key is String && entry.value is String)
.map((entry) => MapEntry(entry.key as String, entry.value as String))
);
6. 内存泄漏风险
dart
class DataCache {
final Map<String, List<String>> _cache = {};
// ❌ 可能导致内存泄漏
void addData(String key, String data) {
_cache.putIfAbsent(key, () => []).add(data);
// 缓存无限增长!
}
// ✅ 限制缓存大小
static const int maxCacheSize = 1000;
static const int maxListSize = 100;
void addDataSafely(String key, String data) {
// 限制Map的大小
if (_cache.length > maxCacheSize) {
// 删除最旧的条目(简化策略)
_cache.remove(_cache.keys.first);
}
// 限制List的大小
List<String> list = _cache.putIfAbsent(key, () => []);
if (list.length > maxListSize) {
list.removeRange(0, list.length ~/ 2); // 删除一半旧数据
}
list.add(data);
}
void cleanup() {
_cache.clear();
}
}
最佳实践
1. 类型安全
dart
// ✅ 明确指定类型
Map<String, int> scores = <String, int>{};
Map<String, List<String>> groups = <String, List<String>>{};
// ✅ 使用泛型保持类型安全
T getValue<T>(Map<String, dynamic> map, String key, T defaultValue) {
var value = map[key];
return value is T ? value : defaultValue;
}
// 使用示例
Map<String, dynamic> data = {'name': 'Alice', 'age': 25};
String name = getValue(data, 'name', '');
int age = getValue(data, 'age', 0);
2. 空安全处理
dart
// ✅ 优先使用非空类型
Map<String, String> nonNullMap = {'key': 'value'};
// ✅ 合理使用可空类型
Map<String, String?> nullableMap = {'key1': 'value1', 'key2': null};
// ✅ 安全的访问模式
String getValue(Map<String, String?> map, String key) {
return map[key] ?? 'default_value';
}
// ✅ 使用containsKey进行精确检查
if (nullableMap.containsKey('key2')) {
String? value = nullableMap['key2'];
print('Key exists with value: ${value ?? 'null'}');
}
3. 高效的Map操作
dart
// ✅ 批量操作
Map<String, int> target = {};
Map<String, int> source = {'a': 1, 'b': 2, 'c': 3};
target.addAll(source); // 比逐个添加高效
// ✅ 使用putIfAbsent避免重复检查
Map<String, List<String>> groups = {};
groups.putIfAbsent('group1', () => []).add('item1');
// ✅ 使用update进行条件更新
Map<String, int> counters = {};
counters.update('count', (value) => value + 1, ifAbsent: () => 1);
// ✅ 使用removeWhere进行批量删除
Map<String, int> scores = {'Alice': 95, 'Bob': 75, 'Charlie': 85};
scores.removeWhere((name, score) => score < 80);
4. 内存管理
dart
// ✅ 及时清理大型Map
class LimitedCache<K, V> {
final Map<K, V> _cache = {};
final int maxSize;
LimitedCache(this.maxSize);
V? get(K key) => _cache[key];
void put(K key, V value) {
if (_cache.length >= maxSize) {
_clearOldest();
}
_cache[key] = value;
}
void _clearOldest() {
if (_cache.isNotEmpty) {
_cache.remove(_cache.keys.first);
}
}
void clear() => _cache.clear();
}
// ✅ 使用弱引用避免内存泄漏(概念示例)
class ResourceManager {
final Map<String, WeakReference<ExpensiveResource>> _resources = {};
ExpensiveResource? getResource(String id) {
WeakReference<ExpensiveResource>? ref = _resources[id];
ExpensiveResource? resource = ref?.target;
if (resource == null) {
_resources.remove(id); // 清理失效的引用
}
return resource;
}
}
class ExpensiveResource {
final String data;
ExpensiveResource(this.data);
}
5. 错误处理
dart
// ✅ 使用异常处理
class SafeMap<K, V> {
final Map<K, V> _map = {};
V? safeGet(K key) {
try {
return _map[key];
} catch (e) {
print('Error accessing key $key: $e');
return null;
}
}
bool safePut(K key, V value) {
try {
_map[key] = value;
return true;
} catch (e) {
print('Error setting key $key: $e');
return false;
}
}
}
// ✅ 验证输入
class ValidatedMap<K, V> {
final Map<K, V> _map = {};
final bool Function(K) keyValidator;
final bool Function(V) valueValidator;
ValidatedMap({
required this.keyValidator,
required this.valueValidator,
});
void put(K key, V value) {
if (!keyValidator(key)) {
throw ArgumentError('Invalid key: $key');
}
if (!valueValidator(value)) {
throw ArgumentError('Invalid value: $value');
}
_map[key] = value;
}
V? get(K key) => _map[key];
}
// 使用示例
var stringIntMap = ValidatedMap<String, int>(
keyValidator: (key) => key.isNotEmpty,
valueValidator: (value) => value >= 0,
);
6. 性能监控
dart
// ✅ 性能监控装饰器
class MonitoredMap<K, V> implements Map<K, V> {
final Map<K, V> _delegate;
int _getCount = 0;
int _putCount = 0;
MonitoredMap(this._delegate);
@override
V? operator [](Object? key) {
_getCount++;
return _delegate[key];
}
@override
void operator []=(K key, V value) {
_putCount++;
_delegate[key] = value;
}
void printStats() {
print('Map Stats: $_getCount gets, $_putCount puts, ${_delegate.length} entries');
}
// 实现其他Map方法...
@override
void addAll(Map<K, V> other) => _delegate.addAll(other);
@override
void addEntries(Iterable<MapEntry<K, V>> newEntries) => _delegate.addEntries(newEntries);
@override
Map<RK, RV> cast<RK, RV>() => _delegate.cast<RK, RV>();
@override
void clear() => _delegate.clear();
@override
bool containsKey(Object? key) => _delegate.containsKey(key);
@override
bool containsValue(Object? value) => _delegate.containsValue(value);
@override
Iterable<MapEntry<K, V>> get entries => _delegate.entries;
@override
void forEach(void Function(K key, V value) f) => _delegate.forEach(f);
@override
bool get isEmpty => _delegate.isEmpty;
@override
bool get isNotEmpty => _delegate.isNotEmpty;
@override
Iterable<K> get keys => _delegate.keys;
@override
int get length => _delegate.length;
@override
Map<K2, V2> map<K2, V2>(MapEntry<K2, V2> Function(K key, V value) f) => _delegate.map(f);
@override
V putIfAbsent(K key, V Function() ifAbsent) => _delegate.putIfAbsent(key, ifAbsent);
@override
V? remove(Object? key) => _delegate.remove(key);
@override
void removeWhere(bool Function(K key, V value) predicate) => _delegate.removeWhere(predicate);
@override
V update(K key, V Function(V value) update, {V Function()? ifAbsent}) =>
_delegate.update(key, update, ifAbsent: ifAbsent);
@override
void updateAll(V Function(K key, V value) update) => _delegate.updateAll(update);
@override
Iterable<V> get values => _delegate.values;
}
总结
Dart的Map类提供了强大而灵活的键值对存储功能。在使用时应该注意:
- 选择合适的实现:根据需求选择HashMap、LinkedHashMap或SplayTreeMap
- 保证类型安全:使用泛型确保类型安全,正确实现自定义键类型的相等性
- 注意性能影响:了解各种操作的时间复杂度,选择高效的操作方式
- 处理边界情况:区分键不存在和值为null,处理空Map和异常情况
- 合理使用高级功能:putIfAbsent、update、removeWhere等方法可以让代码更简洁高效
- 避免常见陷阱:注意键的相等性实现、并发修改、内存泄漏等问题
通过掌握这些API和最佳实践,可以更高效地使用Dart中的Map类型,编写出高质量的代码。
本文档基于Dart 3.x版本整理,如有更新请参考官方文档。