一、map
map 是 Dart 中 List 集合的核心转换方法,作用是遍历列表中的每一个元素,对每个元素执行指定的转换逻辑,最终返回一个新的可迭代对象(Iterable) 。
-
核心特点:不会修改原列表,而是返回新的迭代对象(需要手动转成
List); -
语法:
Iterable<T> map<T>(T Function(E element) convert)convert:转换函数,接收原列表的单个元素,返回转换后的元素;T:转换后元素的类型(可省略,Dart 会自动推导);E:原列表元素的类型。
二、基础用法(必掌握)
1. 基本类型转换
最常见的场景:将列表中的元素做简单转换(如数字转字符串、数值运算等)。
void main() {
// 原列表:整数列表
List<int> numbers = [1, 2, 3, 4, 5];
// 1. 转换:每个数字乘以2 → 返回 Iterable<int>
Iterable<int> doubledIterable = numbers.map((int num) {
return num * 2;
});
// 2. 转成 List(关键:map返回的是Iterable,需用toList()转成List)
List<int> doubledList = doubledIterable.toList();
print("原列表:$numbers"); // 原列表:[1, 2, 3, 4, 5](原列表不变)
print("转换后:$doubledList"); // 转换后:[2, 4, 6, 8, 10]
// 简化写法(箭头函数):单行逻辑推荐用箭头函数
List<String> numToString = numbers.map((num) => num.toString()).toList();
print("数字转字符串:$numToString"); // [1, 2, 3, 4, 5]
}
2. 自定义对象转换
实战中更常用的场景:将自定义对象列表转换为其他格式(如提取对象的某个属性、转成 DTO 等)。
// 定义自定义对象
class User {
final String name;
final int age;
User({required this.name, required this.age});
}
void main() {
List<User> users = [
User(name: "张三", age: 20),
User(name: "李四", age: 25),
User(name: "王五", age: 30),
];
// 场景1:提取所有用户的姓名 → 字符串列表
List<String> userNames = users.map((user) => user.name).toList();
print("用户姓名:$userNames"); // [张三, 李四, 王五]
// 场景2:转换为新的Map列表(如接口请求参数)
List<Map<String, dynamic>> userMaps = users.map((user) {
return {
"username": user.name,
"user_age": user.age,
"is_adult": user.age >= 18, // 新增衍生字段
};
}).toList();
print("转Map列表:$userMaps");
// 输出:[{username: 张三, user_age: 20, is_adult: true}, ...]
}
三、关键注意事项(避坑)
1. 必须用 toList() 转成列表
map 方法返回的是 Iterable(可迭代对象),不是 List,如果直接使用会导致部分 List 方法(如 add、remove)无法调用:
void main() {
List<int> nums = [1,2,3];
// 错误用法:Iterable 没有 add 方法
// nums.map((e) => e*2).add(4);
// 正确用法:先转List
List<int> newNums = nums.map((e) => e*2).toList();
newNums.add(4); // [2,4,6,4]
}
2. 惰性执行特性
map 方法的转换逻辑不会立即执行,而是在遍历 Iterable(如调用 toList()/forEach())时才执行:
void main() {
List<int> nums = [1,2,3];
// 定义map转换,但未执行
Iterable<int> iter = nums.map((e) {
print("执行转换:$e");
return e*2;
});
print("还未执行转换");
// 调用toList()时,才会遍历并执行转换逻辑
List<int> list = iter.toList();
// 输出顺序:
// 还未执行转换
// 执行转换:1
// 执行转换:2
// 执行转换:3
}
3. 原列表修改不影响已生成的 Iterable
map 是基于原列表当时的状态生成迭代对象,后续修改原列表不会改变已生成的 Iterable:
void main() {
List<int> nums = [1,2,3];
Iterable<int> iter = nums.map((e) => e*2);
// 修改原列表
nums.add(4);
// 转换后的列表包含原列表的3个元素(1,2,3),不包含新增的4
List<int> list = iter.toList();
print(list); // [2,4,6]
}
四、高级用法
1. 链式调用
map 可与其他列表方法(where、sort、take 等)链式调用,实现复杂转换:
void main() {
List<int> nums = [1,2,3,4,5,6,7,8];
// 需求:筛选偶数 → 乘以10 → 转字符串 → 取前3个
List<String> result = nums
.where((e) => e % 2 == 0) // 筛选偶数:[2,4,6,8]
.map((e) => e * 10) // 乘以10:[20,40,60,80]
.map((e) => "数值:$e") // 转字符串:["数值:20", ...]
.take(3) // 取前3个:["数值:20", "数值:40", "数值:60"]
.toList();
print(result); // [数值:20, 数值:40, 数值:60]
}
2. 处理空值(null safety)
Dart 空安全下,处理可能包含 null 的列表:
void main() {
List<int?> nums = [1, null, 3, null, 5];
// 方式1:过滤null后转换
List<int> result1 = nums
.where((e) => e != null) // 过滤null
.map((e) => e!) // 非空断言(已过滤,安全)
.toList();
print(result1); // [1,3,5]
// 方式2:给null设置默认值
List<int> result2 = nums.map((e) => e ?? 0).toList();
print(result2); // [1,0,3,0,5]
}
五、map vs forEach(易混淆对比)
很多新手会混淆 map 和 forEach,核心区别如下:
| 特性 | map | forEach |
|---|---|---|
| 核心作用 | 转换元素,返回新的 Iterable | 遍历元素执行操作,无返回值 |
| 返回值 | Iterable<T> | void(无返回值) |
| 是否修改原列表 | 否 | 否(但可在回调中手动修改元素) |
| 典型场景 | 元素类型转换、提取属性 | 遍历执行副作用(如打印、存储) |
void main() {
List<int> nums = [1,2,3];
// map:转换并返回新列表
List<int> mapResult = nums.map((e) => e*2).toList();
// forEach:遍历执行操作,无返回值
nums.forEach((e) {
print("遍历元素:$e"); // 打印每个元素
});
}
总结
- 核心作用:
List.map()是列表元素转换的核心方法,返回 Iterable,需用toList()转成列表; - 关键特性:惰性执行、不修改原列表、支持空安全和链式调用;
- 避坑点:必须转 List 才能使用 List 方法,空列表调用 map 不会报错(返回空 Iterable);
- 使用场景:类型转换、提取对象属性、生成新格式数据(如接口参数)。
掌握 map 方法后,能大幅简化列表转换的代码,是 Dart 开发中最常用的列表操作之一。