Flutter——List.map()

22 阅读2分钟

一、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 方法(如 addremove)无法调用:

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 可与其他列表方法(wheresorttake 等)链式调用,实现复杂转换:

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(易混淆对比)

很多新手会混淆 mapforEach,核心区别如下:

特性mapforEach
核心作用转换元素,返回新的 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"); // 打印每个元素
  });
}

总结

  1. 核心作用List.map() 是列表元素转换的核心方法,返回 Iterable,需用 toList() 转成列表;
  2. 关键特性:惰性执行、不修改原列表、支持空安全和链式调用;
  3. 避坑点:必须转 List 才能使用 List 方法,空列表调用 map 不会报错(返回空 Iterable);
  4. 使用场景:类型转换、提取对象属性、生成新格式数据(如接口参数)。

掌握 map 方法后,能大幅简化列表转换的代码,是 Dart 开发中最常用的列表操作之一。