Dart Map API 详细总结

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

构造函数使用注意事项

⚠️ 重要提醒:

  1. Map.from vs Map.of的区别

    dart 复制代码
    dynamic 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}); // 正确
  2. fromIterables的长度匹配

    dart 复制代码
    List<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);
    }
  3. 身份Map vs 相等性Map

    dart 复制代码
    Map<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

基本属性使用注意事项

⚠️ 重要提醒:

  1. keys和values的顺序

    dart 复制代码
    Map<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);
  2. entries的使用优势

    dart 复制代码
    Map<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

访问和修改使用注意事项

⚠️ 重要提醒:

  1. 空安全访问

    dart 复制代码
    Map<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('键不存在');
    }
  2. putIfAbsent的性能考虑

    dart 复制代码
    Map<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);
  3. update方法的异常处理

    dart 复制代码
    Map<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

添加删除使用注意事项

⚠️ 重要提醒:

  1. addAll的覆盖行为

    dart 复制代码
    Map<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'的值被覆盖
  2. remove返回值的利用

    dart 复制代码
    Map<String, List<String>> cache = {
      'key1': ['value1', 'value2']
    };
    
    // ✅ 利用返回值进行清理
    List<String>? removed = cache.remove('key1');
    removed?.clear(); // 清理被删除的List
  3. 不可变Map的操作限制

    dart 复制代码
    Map<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');

查找检查使用注意事项

⚠️ 重要提醒:

  1. containsKey vs 直接访问的性能

    dart 复制代码
    Map<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');
    }
  2. forEach vs for-in性能

    dart 复制代码
    Map<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]}

转换映射使用注意事项

⚠️ 重要提醒:

  1. map操作的键冲突处理

    dart 复制代码
    Map<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]}
  2. 大Map的转换性能

    dart 复制代码
    Map<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';
    }
  3. 转换中的异常处理

    dart 复制代码
    Map<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类提供了强大而灵活的键值对存储功能。在使用时应该注意:

  1. 选择合适的实现:根据需求选择HashMap、LinkedHashMap或SplayTreeMap
  2. 保证类型安全:使用泛型确保类型安全,正确实现自定义键类型的相等性
  3. 注意性能影响:了解各种操作的时间复杂度,选择高效的操作方式
  4. 处理边界情况:区分键不存在和值为null,处理空Map和异常情况
  5. 合理使用高级功能:putIfAbsent、update、removeWhere等方法可以让代码更简洁高效
  6. 避免常见陷阱:注意键的相等性实现、并发修改、内存泄漏等问题

通过掌握这些API和最佳实践,可以更高效地使用Dart中的Map类型,编写出高质量的代码。


本文档基于Dart 3.x版本整理,如有更新请参考官方文档。

相关推荐
农夫三拳_有点甜2 天前
Dart Lock类来自synchronized包
dart
农夫三拳_有点甜2 天前
Dart 并发编程详细总结1
dart
农夫三拳_有点甜2 天前
Dart 运算符和操作符详细总结
dart
农夫三拳_有点甜2 天前
Dart Timer 全面总结指南
dart
农夫三拳_有点甜2 天前
Dart Class API 详细总结
dart
cowice6 天前
Dart基础知识
flutter·dart
AhhhhDong13 天前
记录工作中遇到的几个Dart语言的坑和bug
dart
叽哥18 天前
Flutter面试:Dart基础2
flutter·面试·dart
fouryears_2341722 天前
Flutter InheritedWidget 详解:从生命周期到数据流动的完整解析
开发语言·flutter·客户端·dart