Dart List API 详细总结
基于Dart官方API文档与实践经验整理
目录
概述
在Dart中,List是一个有序的对象集合,类似于其他语言中的数组。List具有以下特性:
- 有序性: 元素按照插入顺序排列
- 可重复: 允许存储重复元素
- 动态大小: 可以动态增长和收缩
- 泛型支持: 支持类型安全的泛型操作
- 索引访问: 通过索引快速访问元素
创建List
1. 字面量语法创建
// 创建包含初始值的List
List<int> numbers = [1, 2, 3, 4, 5];
List<String> names = ['Alice', 'Bob', 'Charlie'];
// 创建空List
List<int> emptyNumbers = <int>[];
List<String> emptyNames = <String>[];
// 类型推断
var fruits = ['apple', 'banana', 'orange']; // List<String>
var scores = [95, 87, 92]; // List<int>
2. 构造函数创建
// 创建空List
List<int> emptyList = List<int>.empty();
// 参数: growable (bool, 默认false) - 是否可增长
// 创建指定长度的List,填充默认值
List<int> filledList = List<int>.filled(5, 0); // [0, 0, 0, 0, 0]
// 参数:
// length (int) - List的长度
// fill (E) - 填充的值
// growable (bool, 默认false) - 是否可增长
// 创建可增长的List
List<int> growableList = List<int>.empty(growable: true);
// 参数: growable (bool) - 设置为true创建可增长的List
// 使用生成器创建List
List<int> generatedList = List<int>.generate(5, (index) => index * 2);
// 参数:
// length (int) - List的长度
// generator (E Function(int)) - 生成器函数,接收索引返回元素
// growable (bool, 默认true) - 是否可增长
// 结果: [0, 2, 4, 6, 8]
// 从其他可迭代对象创建
List<int> fromIterable = List<int>.from([1, 2, 3]);
// 参数:
// elements (Iterable) - 源可迭代对象
// growable (bool, 默认true) - 是否可增长
List<int> ofIterable = List<int>.of([1, 2, 3]);
// 参数:
// elements (Iterable<E>) - 源可迭代对象(类型安全)
// growable (bool, 默认true) - 是否可增长
构造函数使用注意事项
⚠️ 重要提醒:
-
growable参数的影响:
// 固定长度List - 不能添加或删除元素 List<int> fixedList = List<int>.filled(3, 0, growable: false); // fixedList.add(4); // 运行时错误! // 可增长List - 可以自由添加删除 List<int> growableList = List<int>.filled(3, 0, growable: true); growableList.add(4); // 正常工作 -
List.filled的引用陷阱:
// ❌ 危险:所有元素共享同一个对象引用 List<List<int>> matrix = List.filled(3, <int>[]); matrix[0].add(1); // 所有行都会被修改! [[1, 1], [1, 1], [1, 1]] // ✅ 正确:每个位置创建新对象 List<List<int>> matrix = List.generate(3, (index) => <int>[]); matrix[0].add(1); // 只修改第一行 [[1], [], []] -
List.from vs List.of的区别:
dynamic dynamicList = [1, 2, 3]; //✅ 类型转换更宽松 List.from 接受任意Iterable,可能有运行时类型错误 List<int> fromList = List<int>.from(dynamicList); // ❌ List.of 要求类型匹配,编译时类型安全 // List<int> ofList = List<int>.of(dynamicList); // 编译错误 List<int> ofList = List<int>.of(<int>[1, 2, 3]); // 正确 // 示例数据 List<dynamic> mixedList = [1, 2, 3, 'hello']; List<int> intList = [1, 2, 3, 4, 5]; // List.from - 适用于类型转换 List<String> stringList1 = List<String>.from(mixedList.map((e) => e.toString())); // List.of - 适用于相同类型的复制 List<int> intList2 = List<int>.of(intList); // List.from 可以处理运行时类型转换 List<int> fromMixed = List<int>.from(mixedList.where((e) => e is int)); // List.of 在编译时就要求类型匹配 // List<int> ofMixed = List<int>.of(mixedList); // 编译错误! -
生成器函数的性能考虑:
// ❌ 避免:在生成器中执行昂贵操作 List<String> expensiveList = List.generate(1000, (i) { return fetchDataFromNetwork(i); // 同步网络请求! }); // ✅ 推荐:生成器应该快速执行 List<int> efficientList = List.generate(1000, (i) => i * i);
基本属性
List<String> fruits = ['apple', 'banana', 'orange'];
// 长度
print(fruits.length); // 3
// 是否为空
print(fruits.isEmpty); // false
print(fruits.isNotEmpty); // true
// 第一个和最后一个元素
print(fruits.first); // 'apple'
print(fruits.last); // 'orange'
// 单个元素(当且仅当List只有一个元素时)
List<String> single = ['only'];
print(single.single); // 'only'
// 反转视图
print(fruits.reversed); // (orange, banana, apple)
访问和修改元素
List<String> colors = ['red', 'green', 'blue'];
// 通过索引访问
print(colors[0]); // 'red'
print(colors[1]); // 'green'
// 参数: index (int) - 要访问的元素索引,从0开始
// 安全访问(避免越界)
String? getElementAt(List<String> list, int index) {
return index >= 0 && index < list.length ? list[index] : null;
}
// 修改元素
colors[1] = 'yellow';
print(colors); // [red, yellow, blue]
// 参数: index (int) - 要修改的元素索引
// 使用elementAt方法
print(colors.elementAt(2)); // 'blue'
// 参数: index (int) - 要访问的元素索引
// 返回: E - 指定位置的元素
// 注意: 如果索引越界会抛出RangeError异常
访问和修改使用注意事项
⚠️ 重要提醒:
-
索引越界检查:
List<String> items = ['a', 'b', 'c']; // ❌ 危险:可能越界 // print(items[5]); // 抛出RangeError异常 // ✅ 安全访问方式 if (index >= 0 && index < items.length) { print(items[index]); } // ✅ 使用扩展方法进行安全访问 extension SafeList<T> on List<T> { T? safeGet(int index) => index >= 0 && index < length ? this[index] : null; } print(items.safeGet(5)); // 返回null而不是异常 -
负数索引处理:
List<int> numbers = [1, 2, 3, 4, 5]; // ❌ Dart不支持负数索引(不像Python) // print(numbers[-1]); // 抛出RangeError异常 // ✅ 正确的最后元素访问方式 print(numbers[numbers.length - 1]); // 5 print(numbers.last); // 5 -
空List访问陷阱:
List<String> emptyList = []; // ❌ 这些都会抛出异常 // print(emptyList.first); // StateError // print(emptyList.last); // StateError // print(emptyList.single); // StateError // ✅ 安全的访问方式 if (emptyList.isNotEmpty) { print(emptyList.first); } // ✅ 使用try-catch处理 try { print(emptyList.first); } catch (e) { print('List为空'); } -
修改固定长度List的限制:
List<int> fixedList = List.filled(3, 0, growable: false); // ✅ 可以修改现有元素 fixedList[0] = 10; // 正常工作 // ❌ 不能改变长度 // fixedList.add(4); // UnsupportedError // fixedList.removeAt(0); // UnsupportedError // fixedList.length = 5; // UnsupportedError
添加元素
List<int> numbers = [1, 2, 3];
// 添加单个元素到末尾
numbers.add(4);
// 参数: value (E) - 要添加的元素
// 返回: void
print(numbers); // [1, 2, 3, 4]
// 添加多个元素到末尾
numbers.addAll([5, 6, 7]);
// 参数: iterable (Iterable<E>) - 要添加的可迭代对象
// 返回: void
print(numbers); // [1, 2, 3, 4, 5, 6, 7]
// 在指定位置插入单个元素
numbers.insert(0, 0);
// 参数:
// index (int) - 插入位置的索引
// element (E) - 要插入的元素
// 返回: void
print(numbers); // [0, 1, 2, 3, 4, 5, 6, 7]
// 在指定位置插入多个元素
numbers.insertAll(1, [-1, -2]);
// 参数:
// index (int) - 插入位置的索引
// iterable (Iterable<E>) - 要插入的可迭代对象
// 返回: void
print(numbers); // [0, -1, -2, 1, 2, 3, 4, 5, 6, 7]
添加元素使用注意事项
⚠️ 重要提醒:
-
固定长度List不能添加元素:
List<int> fixedList = List.filled(3, 0, growable: false); // ❌ 这些操作都会抛出UnsupportedError异常 // fixedList.add(4); // fixedList.addAll([4, 5]); // fixedList.insert(1, 10); // fixedList.insertAll(1, [10, 20]); -
insert操作的性能影响:
List<int> numbers = [1, 2, 3, 4, 5]; // ❌ 在开头插入效率低(O(n)时间复杂度) numbers.insert(0, 0); // 需要移动所有现有元素 // ✅ 在末尾添加效率高(O(1)时间复杂度) numbers.add(6); // 直接添加到末尾 -
批量添加的性能优势:
List<int> target = [1, 2, 3]; List<int> source = [4, 5, 6, 7, 8]; // ❌ 效率低:多次内存分配 for (int item in source) { target.add(item); } // ✅ 效率高:一次性分配内存 target.addAll(source); -
空值和null安全:
List<String?> nullableList = ['a', 'b']; List<String> nonNullList = ['x', 'y']; // ✅ 可空列表可以添加null nullableList.add(null); // 正常工作 // ❌ 非空列表不能添加null // nonNullList.add(null); // 编译错误
删除元素
List<String> fruits = ['apple', 'banana', 'orange', 'apple'];
// 删除第一个匹配的元素
fruits.remove('apple');
// 参数: value (Object?) - 要删除的元素值
// 返回: bool - 如果成功删除返回true,否则返回false
print(fruits); // [banana, orange, apple]
// 根据索引删除
fruits.removeAt(1);
// 参数: index (int) - 要删除元素的索引
// 返回: E - 被删除的元素
print(fruits); // [banana, apple]
// 删除最后一个元素
String last = fruits.removeLast();
// 参数: 无
// 返回: E - 被删除的最后一个元素
// 注意: 如果List为空会抛出异常
print(last); // 'apple'
print(fruits); // [banana]
// 删除范围内的元素
List<int> numbers = [1, 2, 3, 4, 5];
numbers.removeRange(1, 4); // 删除索引1到3的元素
// 参数:
// start (int) - 开始索引(包含)
// end (int) - 结束索引(不包含)
// 返回: void
print(numbers); // [1, 5]
// 根据条件删除
List<int> values = [1, 2, 3, 4, 5, 6];
values.removeWhere((element) => element % 2 == 0);
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被删除
// 返回: void
print(values); // [1, 3, 5]
// 保留符合条件的元素
List<int> scores = [85, 92, 78, 96, 88];
scores.retainWhere((score) => score >= 90);
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被保留
// 返回: void
print(scores); // [92, 96]
// 清空List
fruits.clear();
// 参数: 无
// 返回: void
print(fruits); // []
删除元素使用注意事项
⚠️ 重要提醒:
-
固定长度List不能删除元素:
List<int> fixedList = List.filled(3, 1, growable: false); // ❌ 这些操作都会抛出UnsupportedError异常 // fixedList.remove(1); // fixedList.removeAt(0); // fixedList.removeLast(); // fixedList.removeRange(0, 2); // fixedList.removeWhere((x) => x == 1); // fixedList.clear(); -
删除操作中的索引变化陷阱:
List<int> numbers = [1, 2, 3, 4, 5]; // ❌ 危险:正向遍历删除会跳过元素 for (int i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) { numbers.removeAt(i); // 删除后索引会变化! } } // ✅ 正确:反向遍历删除 for (int i = numbers.length - 1; i >= 0; i--) { if (numbers[i] % 2 == 0) { numbers.removeAt(i); } } // ✅ 更好:使用removeWhere numbers.removeWhere((x) => x % 2 == 0); -
removeLast在空List上的异常:
List<String> emptyList = []; // ❌ 空List调用removeLast会抛出StateError // String last = emptyList.removeLast(); // 异常! // ✅ 安全的删除方式 if (emptyList.isNotEmpty) { String last = emptyList.removeLast(); } -
remove方法的查找性能:
List<String> largeList = List.generate(10000, (i) => 'item$i'); // ❌ 效率低:O(n)查找 + O(n)删除 largeList.remove('item9999'); // 需要遍历整个列表 // ✅ 如果知道索引,直接使用removeAt if (index != -1) { largeList.removeAt(index); // O(n)但避免了查找 } -
removeWhere的安全性:
List<int> numbers = [1, 2, 3, 4, 5]; // ✅ removeWhere是安全的,不会有索引问题 numbers.removeWhere((x) => x > 3); // ✅ 但要注意函数的副作用 numbers.removeWhere((x) { print('检查: $x'); // 副作用:打印 return x % 2 == 0; });
查找和检查
List<String> animals = ['cat', 'dog', 'bird', 'cat'];
// 检查是否包含元素
print(animals.contains('dog')); // true
print(animals.contains('fish')); // false
// 参数: element (Object?) - 要查找的元素
// 返回: bool - 如果包含该元素返回true,否则返回false
// 查找元素索引
print(animals.indexOf('cat')); // 0
// 参数: element (E) - 要查找的元素
// start (int, 可选) - 开始搜索的索引,默认为0
// 返回: int - 元素的索引,如果未找到返回-1
print(animals.lastIndexOf('cat')); // 3
// 参数: element (E) - 要查找的元素
// start (int?, 可选) - 开始搜索的索引,默认为null(从末尾开始)
// 返回: int - 元素的最后一个索引,如果未找到返回-1
print(animals.indexOf('fish')); // -1 (未找到)
// 查找符合条件的元素
print(animals.indexWhere((animal) => animal.startsWith('b'))); // 2
// 参数: test (bool Function(E)) - 测试函数
// start (int, 可选) - 开始搜索的索引,默认为0
// 返回: int - 第一个符合条件元素的索引,如果未找到返回-1
print(animals.lastIndexWhere((animal) => animal.length == 3)); // 3
// 参数: test (bool Function(E)) - 测试函数
// start (int?, 可选) - 开始搜索的索引,默认为null
// 返回: int - 最后一个符合条件元素的索引,如果未找到返回-1
// 检查所有元素是否符合条件
List<int> numbers = [2, 4, 6, 8];
print(numbers.every((n) => n % 2 == 0)); // true
// 参数: test (bool Function(E)) - 测试函数
// 返回: bool - 如果所有元素都符合条件返回true,否则返回false
// 检查是否有元素符合条件
List<int> mixed = [1, 3, 5, 6];
print(mixed.any((n) => n % 2 == 0)); // true
// 参数: test (bool Function(E)) - 测试函数
// 返回: bool - 如果至少有一个元素符合条件返回true,否则返回false
查找和检查使用注意事项
⚠️ 重要提醒:
-
contains方法的对象比较:
List<String> names = ['Alice', 'Bob', 'Charlie']; List<List<int>> nested = [[1, 2], [3, 4]]; // ✅ 基本类型比较正常工作 print(names.contains('Alice')); // true // ❌ 对象引用比较可能不符合预期 print(nested.contains([1, 2])); // false! 不同的对象引用 // ✅ 正确的对象比较方式 List<int> target = [1, 2]; nested.add(target); print(nested.contains(target)); // true,同一个引用 -
indexOf的性能和限制:
List<String> largeList = List.generate(100000, (i) => 'item$i'); // ❌ 线性搜索,大列表性能差 int index = largeList.indexOf('item99999'); // O(n)时间复杂度 // ✅ 如果需要频繁查找,考虑使用Map Map<String, int> indexMap = {}; for (int i = 0; i < largeList.length; i++) { indexMap[largeList[i]] = i; } int fastIndex = indexMap['item99999'] ?? -1; // O(1)时间复杂度 -
every和any的短路求值:
List<int> numbers = [1, 2, 3, 4, 5]; // ✅ every遇到第一个false就停止 bool allEven = numbers.every((n) { print('检查: $n'); // 只会打印 '检查: 1' return n % 2 == 0; }); // ✅ any遇到第一个true就停止 bool hasEven = numbers.any((n) { print('检查: $n'); // 只会打印 '检查: 1' 和 '检查: 2' return n % 2 == 0; }); -
空List的边界情况:
List<int> emptyList = []; // ✅ 这些方法在空List上是安全的 print(emptyList.contains(1)); // false print(emptyList.indexOf(1)); // -1 print(emptyList.every((x) => x > 0)); // true! 空集合的全称量词为真 print(emptyList.any((x) => x > 0)); // false -
indexWhere的起始位置陷阱:
List<String> words = ['apple', 'banana', 'apple', 'cherry']; // ✅ 找到第一个匹配项 int firstApple = words.indexWhere((w) => w == 'apple'); // 0 // ✅ 从指定位置开始查找 int secondApple = words.indexWhere((w) => w == 'apple', firstApple + 1); // 2 // ❌ 忘记更新起始位置会重复找到同一个元素 int stillFirst = words.indexWhere((w) => w == 'apple', 0); // 还是0
转换和映射
List<int> numbers = [1, 2, 3, 4, 5];
// map: 转换每个元素
List<int> doubled = numbers.map((n) => n * 2).toList();
// 参数: f (T Function(E)) - 转换函数,接收元素返回转换后的值
// 返回: Iterable<T> - 转换后的可迭代对象
print(doubled); // [2, 4, 6, 8, 10]
// where: 过滤元素
List<int> evenNumbers = numbers.where((n) => n % 2 == 0).toList();
// 参数: test (bool Function(E)) - 测试函数,返回true的元素将被保留
// 返回: Iterable<E> - 过滤后的可迭代对象
print(evenNumbers); // [2, 4]
// expand: 展开元素
List<List<int>> nested = [[1, 2], [3, 4], [5]];
List<int> flattened = nested.expand((list) => list).toList();
// 参数: f (Iterable<T> Function(E)) - 展开函数,接收元素返回可迭代对象
// 返回: Iterable<T> - 展开后的可迭代对象
print(flattened); // [1, 2, 3, 4, 5]
// fold: 累积操作
int sum = numbers.fold(0, (prev, element) => prev + element);
// 参数:
// initialValue (T) - 初始值
// combine (T Function(T, E)) - 累积函数,接收前一个值和当前元素
// 返回: T - 累积结果
print(sum); // 15
// reduce: 归约操作
int product = numbers.reduce((value, element) => value * element);
// 参数: combine (E Function(E, E)) - 归约函数,接收两个元素返回归约结果
// 返回: E - 归约结果
// 注意: 如果List为空会抛出异常
print(product); // 120
// 转换为其他集合类型
Set<int> uniqueNumbers = numbers.toSet();
// 参数: 无
// 返回: Set<E> - 转换后的Set集合
print(uniqueNumbers); // {1, 2, 3, 4, 5}
转换和映射使用注意事项
⚠️ 重要提醒:
-
惰性求值的陷阱:
List<int> numbers = [1, 2, 3, 4, 5]; // ❌ 这些操作是惰性的,不会立即执行 Iterable<int> doubled = numbers.map((n) => n * 2); Iterable<int> evens = numbers.where((n) => n % 2 == 0); // ✅ 需要调用toList()才会实际执行 List<int> doubledList = numbers.map((n) => n * 2).toList(); List<int> evensList = numbers.where((n) => n % 2 == 0).toList(); // ❌ 多次迭代会重复执行转换函数 print(doubled.length); // 执行一次map print(doubled.first); // 再次执行map! // ✅ 转换为List避免重复计算 List<int> cached = doubled.toList(); print(cached.length); // 不会重复执行 print(cached.first); // 不会重复执行 -
map函数的副作用问题:
List<int> numbers = [1, 2, 3]; int counter = 0; // ❌ 避免在map中使用副作用 Iterable<int> mapped = numbers.map((n) { counter++; // 副作用! return n * 2; }); // 由于惰性求值,counter的值可能不确定 print(counter); // 可能是0 mapped.toList(); // 现在counter变成3 print(counter); // 3 // ✅ 如果需要副作用,使用forEach numbers.forEach((n) { counter++; // 处理逻辑 }); -
where过滤的性能考虑:
List<int> largeList = List.generate(1000000, (i) => i); // ❌ 多个where链式调用效率低 List<int> result = largeList .where((n) => n > 100) // 第一次遍历 .where((n) => n < 1000) // 第二次遍历 .where((n) => n % 2 == 0) // 第三次遍历 .toList(); // ✅ 合并条件,只遍历一次 List<int> efficient = largeList .where((n) => n > 100 && n < 1000 && n % 2 == 0) .toList(); -
reduce和fold的区别与陷阱:
List<int> numbers = [1, 2, 3, 4, 5]; List<int> emptyList = []; // ✅ fold可以处理空列表 int sum1 = numbers.fold(0, (a, b) => a + b); // 15 int sum2 = emptyList.fold(0, (a, b) => a + b); // 0 // ❌ reduce在空列表上会抛出异常 int sum3 = numbers.reduce((a, b) => a + b); // 15 // int sum4 = emptyList.reduce((a, b) => a + b); // StateError! // ✅ 安全的reduce使用 if (emptyList.isNotEmpty) { int sum = emptyList.reduce((a, b) => a + b); } -
expand的嵌套展开理解:
List<String> words = ['hello', 'world']; // ✅ expand将每个元素转换为可迭代对象,然后展开 List<String> chars = words.expand((word) => word.split('')).toList(); print(chars); // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'] // ❌ 常见错误:返回非可迭代对象 // words.expand((word) => word.length); // 编译错误! // ✅ 正确:返回可迭代对象 List<int> lengths = words.expand((word) => [word.length]).toList(); -
类型转换的安全性:
List<dynamic> mixed = [1, 'hello', 2, 'world', 3]; // ❌ 不安全的类型转换 // List<int> numbers = mixed.map((x) => x as int).toList(); // 运行时错误! // ✅ 安全的类型过滤和转换 List<int> numbers = mixed .where((x) => x is int) .map((x) => x as int) .toList(); // ✅ 更安全的方式 List<int> safeNumbers = mixed .whereType<int>() .toList();
排序和反转
List<int> numbers = [5, 2, 8, 1, 9];
// 升序排序
numbers.sort();
// 参数: compare (int Function(E, E)?, 可选) - 比较函数,默认使用元素的自然排序
// 返回: void
print(numbers); // [1, 2, 5, 8, 9]
// 自定义排序
List<String> names = ['Alice', 'bob', 'Charlie'];
names.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
// 参数: compare (int Function(E, E)) - 比较函数
// 返回负数:a < b,返回0:a == b,返回正数:a > b
// 返回: void
print(names); // [Alice, bob, Charlie]
// 反转List
numbers = [1, 2, 3, 4, 5];
List<int> reversed = numbers.reversed.toList();
// reversed属性:
// 参数: 无
// 返回: Iterable<E> - 反转后的可迭代对象(惰性视图)
print(reversed); // [5, 4, 3, 2, 1]
// 洗牌
numbers.shuffle();
// 参数: random (Random?, 可选) - 随机数生成器,默认使用系统随机数
// 返回: void
print(numbers); // 随机顺序
高级操作
子列表操作
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 获取子列表
List<int> sublist = numbers.sublist(2, 6);
// 参数:
// start (int) - 开始索引(包含)
// end (int?, 可选) - 结束索引(不包含),默认为length
// 返回: List<E> - 新的子列表
print(sublist); // [3, 4, 5, 6]
// 获取范围
List<int> range = numbers.getRange(1, 4).toList();
// 参数:
// start (int) - 开始索引(包含)
// end (int) - 结束索引(不包含)
// 返回: Iterable<E> - 范围内元素的可迭代对象
print(range); // [2, 3, 4]
// 设置范围
numbers.setRange(0, 3, [10, 20, 30]);
// 参数:
// start (int) - 开始索引(包含)
// end (int) - 结束索引(不包含)
// iterable (Iterable<E>) - 要设置的值
// skipCount (int, 可选) - 跳过源iterable的元素数量,默认为0
// 返回: void
print(numbers); // [10, 20, 30, 4, 5, 6, 7, 8, 9, 10]
// 替换范围
numbers.replaceRange(1, 4, [100, 200]);
// 参数:
// start (int) - 开始索引(包含)
// end (int) - 结束索引(不包含)
// replacement (Iterable<E>) - 替换的新元素
// 返回: void
print(numbers); // [10, 100, 200, 5, 6, 7, 8, 9, 10]
填充和复制
List<int> numbers = [1, 2, 3, 4, 5];
// 填充指定值
numbers.fillRange(1, 4, 0);
// 参数:
// start (int) - 开始索引(包含)
// end (int) - 结束索引(不包含)
// fillValue (E) - 填充的值
// 返回: void
print(numbers); // [1, 0, 0, 0, 5]
// 设置长度
numbers.length = 3;
// 参数: newLength (int) - 新的长度
// 返回: void
// 注意: 如果新长度小于当前长度,会截断;如果大于当前长度,会用null填充
print(numbers); // [1, 0, 0]
numbers.length = 6; // 扩展长度,新元素为null
print(numbers); // [1, 0, 0, null, null, null]
异步操作
List<int> numbers = [1, 2, 3, 4, 5];
// 异步遍历
await Future.forEach(numbers, (number) async {
await Future.delayed(Duration(milliseconds: 100));
print(number);
});
// Future.forEach参数:
// elements (Iterable<T>) - 要遍历的可迭代对象
// f (Future Function(T)) - 异步处理函数
// 返回: Future<void> - 完成所有异步操作的Future
// 异步映射
List<String> results = await Future.wait(
numbers.map((n) async {
await Future.delayed(Duration(milliseconds: 50));
return 'Number: $n';
})
);
// Future.wait参数:
// futures (Iterable<Future<T>>) - Future对象的可迭代集合
// eagerError (bool, 可选) - 是否在第一个错误时立即失败,默认为false
// 返回: Future<List<T>> - 包含所有结果的Future
print(results);
高级操作使用注意事项
⚠️ 重要提醒:
-
sublist的内存复制:
List<int> largeList = List.generate(1000000, (i) => i); // ❌ sublist会创建新的List,消耗额外内存 List<int> sub = largeList.sublist(0, 100000); // 复制10万个元素 // ✅ 如果只是读取,考虑使用getRange Iterable<int> range = largeList.getRange(0, 100000); // 惰性视图 // ✅ 或者使用ListView.builder等惰性组件 -
setRange的越界和长度检查:
List<int> target = [1, 2, 3, 4, 5]; List<int> source = [10, 20, 30]; // ❌ 越界操作会抛出异常 // target.setRange(3, 7, source); // RangeError: 结束索引超出范围 // ❌ 源数据不足也会抛出异常 // target.setRange(0, 5, source); // StateError: 源数据不够 // ✅ 正确的使用方式 if (source.length >= 3) { target.setRange(0, 3, source); } -
replaceRange的性能影响:
List<int> numbers = List.generate(10000, (i) => i); // ❌ 在列表前部替换会导致大量元素移动 numbers.replaceRange(0, 100, [999]); // O(n)操作,移动9900个元素 // ✅ 在列表后部操作效率更高 numbers.replaceRange(9900, 10000, [999]); // 只移动少量元素 -
fillRange的引用问题:
List<List<int>> matrix = List.generate(3, (i) => [1, 2, 3]); List<int> fillValue = [0, 0, 0]; // ❌ 所有位置会共享同一个对象引用 matrix.fillRange(0, 3, fillValue); matrix[0][0] = 999; // 所有行的第一个元素都变成999! // ✅ 每次创建新对象 for (int i = 0; i < 3; i++) { matrix[i] = [0, 0, 0]; // 每个位置都是独立的对象 } -
length setter的陷阱:
List<String?> items = ['a', 'b', 'c']; // ✅ 缩短长度会丢失数据 items.length = 2; print(items); // ['a', 'b'] - 'c'被永久丢失 // ✅ 扩展长度会用null填充 items.length = 5; print(items); // ['a', 'b', null, null, null] // ❌ 固定长度List不能改变长度 List<int> fixed = List.filled(3, 0, growable: false); // fixed.length = 5; // UnsupportedError -
异步操作的并发控制:
List<int> numbers = List.generate(1000, (i) => i); // ❌ 可能创建过多并发请求 List<String> results = await Future.wait( numbers.map((n) => expensiveNetworkCall(n)) ); // 1000个并发请求! // ✅ 控制并发数量 List<String> controlledResults = []; for (int i = 0; i < numbers.length; i += 10) { List<String> batch = await Future.wait( numbers.skip(i).take(10).map((n) => expensiveNetworkCall(n)) ); controlledResults.addAll(batch); } -
异步操作的错误处理:
List<int> numbers = [1, 2, 3, 4, 5]; // ❌ 一个失败会导致全部失败 try { await Future.wait(numbers.map((n) => riskyOperation(n))); } catch (e) { // 所有操作都被取消 } // ✅ 单独处理每个操作的错误 List<String?> results = await Future.wait( numbers.map((n) async { try { return await riskyOperation(n); } catch (e) { return null; // 或者其他默认值 } }) );
方法参数速查表
构造方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
List.empty() | growable: bool | 是否可增长,默认false | List<E> |
List.filled() | length: int fill: E growable: bool | 长度、填充值、是否可增长 | List<E> |
List.generate() | length: int generator: E Function(int) growable: bool | 长度、生成器函数、是否可增长 | List<E> |
List.from() | elements: Iterable growable: bool | 源集合、是否可增长 | List<E> |
List.of() | elements: Iterable<E> growable: bool | 源集合(类型安全)、是否可增长 | List<E> |
访问和修改方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
[] | index: int | 要访问的索引 | E |
[]= | index: int value: E | 要修改的索引、新值 | void |
elementAt() | index: int | 要访问的索引 | E |
添加方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
add() | value: E | 要添加的元素 | void |
addAll() | iterable: Iterable<E> | 要添加的可迭代对象 | void |
insert() | index: int element: E | 插入位置、要插入的元素 | void |
insertAll() | index: int iterable: Iterable<E> | 插入位置、要插入的可迭代对象 | void |
删除方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
remove() | value: Object? | 要删除的元素值 | bool |
removeAt() | index: int | 要删除的索引 | E |
removeLast() | 无 | 删除最后一个元素 | E |
removeRange() | start: int end: int | 开始索引、结束索引 | void |
removeWhere() | test: bool Function(E) | 测试函数 | void |
retainWhere() | test: bool Function(E) | 测试函数 | void |
clear() | 无 | 清空列表 | void |
查找和检查方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
contains() | element: Object? | 要查找的元素 | bool |
indexOf() | element: E start: int? | 要查找的元素、开始位置 | int |
lastIndexOf() | element: E start: int? | 要查找的元素、开始位置 | int |
indexWhere() | test: bool Function(E) start: int | 测试函数、开始位置 | int |
lastIndexWhere() | test: bool Function(E) start: int? | 测试函数、开始位置 | int |
every() | test: bool Function(E) | 测试函数 | bool |
any() | test: bool Function(E) | 测试函数 | bool |
转换和映射方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
map() | f: T Function(E) | 转换函数 | Iterable<T> |
where() | test: bool Function(E) | 过滤函数 | Iterable<E> |
expand() | f: Iterable<T> Function(E) | 展开函数 | Iterable<T> |
fold() | initialValue: T combine: T Function(T, E) | 初始值、累积函数 | T |
reduce() | combine: E Function(E, E) | 归约函数 | E |
toSet() | 无 | 转换为Set | Set<E> |
排序和反转方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
sort() | compare: int Function(E, E)? | 比较函数 | void |
shuffle() | random: Random? | 随机数生成器 | void |
reversed | 无 | 反转视图(属性) | Iterable<E> |
高级操作方法
| 方法 | 参数 | 说明 | 返回值 |
|---|---|---|---|
sublist() | start: int end: int? | 开始索引、结束索引 | List<E> |
getRange() | start: int end: int | 开始索引、结束索引 | Iterable<E> |
setRange() | start: int end: int iterable: Iterable<E> skipCount: int | 开始索引、结束索引、源数据、跳过数量 | void |
replaceRange() | start: int end: int replacement: Iterable<E> | 开始索引、结束索引、替换数据 | void |
fillRange() | start: int end: int fillValue: E | 开始索引、结束索引、填充值 | void |
性能考虑
时间复杂度
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
[] (索引访问) | O(1) | 直接内存访问 |
add() | O(1)* | 摊销常数时间 |
insert(0, element) | O(n) | 需要移动所有元素 |
removeAt(0) | O(n) | 需要移动所有元素 |
contains() | O(n) | 线性搜索 |
indexOf() | O(n) | 线性搜索 |
性能优化建议
// 1. 预分配容量(如果已知大小)
List<int> numbers = List<int>.filled(1000, 0);
// 2. 使用addAll而不是多次add
List<int> source = [1, 2, 3, 4, 5];
List<int> target = [];
// 好的做法
target.addAll(source);
// 避免的做法
for (int item in source) {
target.add(item);
}
// 3. 从后往前删除元素
List<int> items = [1, 2, 3, 4, 5];
for (int i = items.length - 1; i >= 0; i--) {
if (items[i] % 2 == 0) {
items.removeAt(i);
}
}
// 4. 使用removeWhere替代循环删除
items.removeWhere((item) => item % 2 == 0);
常见陷阱和错误
1. 并发修改异常(ConcurrentModificationError)
List<int> numbers = [1, 2, 3, 4, 5];
// ❌ 在遍历时修改List会抛出异常
try {
for (int number in numbers) {
if (number % 2 == 0) {
numbers.remove(number); // ConcurrentModificationError!
}
}
} catch (e) {
print('错误: $e');
}
// ✅ 正确的方式:使用removeWhere
numbers.removeWhere((number) => number % 2 == 0);
// ✅ 或者反向遍历索引
for (int i = numbers.length - 1; i >= 0; i--) {
if (numbers[i] % 2 == 0) {
numbers.removeAt(i);
}
}
2. 浅拷贝陷阱
List<List<int>> original = [[1, 2], [3, 4]];
// ❌ 浅拷贝,内部List仍然共享引用
List<List<int>> shallowCopy = List.from(original);
shallowCopy[0].add(999); // 原始List也被修改!
print(original); // [[1, 2, 999], [3, 4]]
// ✅ 深拷贝
List<List<int>> deepCopy = original.map((list) => List.from(list)).toList();
deepCopy[0].add(888); // 只修改拷贝
print(original); // [[1, 2, 999], [3, 4]] - 保持不变
3. 类型转换错误
// ❌ 运行时类型错误
dynamic dynamicList = ['1', '2', '3'];
try {
List<int> numbers = List<int>.from(dynamicList); // 运行时错误!
} catch (e) {
print('类型转换错误: $e');
}
// ✅ 安全的类型转换
List<int> safeNumbers = (dynamicList as List)
.where((item) => item is String)
.map((item) => int.tryParse(item))
.where((item) => item != null)
.cast<int>()
.toList();
4. 内存泄漏风险
class DataManager {
List<String> _cache = [];
// ❌ 可能导致内存泄漏
void addData(String data) {
_cache.add(data);
// 缓存无限增长!
}
// ✅ 限制缓存大小
void addDataSafely(String data) {
_cache.add(data);
if (_cache.length > 1000) {
_cache.removeRange(0, 500); // 移除一半旧数据
}
}
// ✅ 或者使用LRU缓存策略
void addWithLRU(String data) {
_cache.remove(data); // 移除已存在的
_cache.add(data); // 添加到末尾
if (_cache.length > 100) {
_cache.removeAt(0); // 移除最旧的
}
}
}
5. 性能陷阱
// ❌ 频繁的List扩容
List<int> inefficient = [];
for (int i = 0; i < 100000; i++) {
inefficient.add(i); // 多次内存重新分配
}
// ✅ 预分配容量
List<int> efficient = List<int>.filled(100000, 0);
for (int i = 0; i < 100000; i++) {
efficient[i] = i; // 直接赋值,无需扩容
}
// ✅ 或者使用generate
List<int> generated = List.generate(100000, (i) => i);
6. 空值处理错误
List<String?> nullableList = ['a', null, 'c'];
// ❌ 可能的空指针异常
try {
List<int> lengths = nullableList.map((s) => s!.length).toList(); // 异常!
} catch (e) {
print('空指针异常: $e');
}
// ✅ 正确的空值处理
List<int> safeLengths = nullableList
.where((s) => s != null)
.map((s) => s!.length)
.toList();
// ✅ 或者提供默认值
List<int> lengthsWithDefault = nullableList
.map((s) => s?.length ?? 0)
.toList();
7. 异步操作的时序问题
List<String> results = [];
// ❌ 异步操作可能导致结果乱序
Future<void> processDataBadly(List<int> data) async {
for (int item in data) {
// 异步操作,结果可能乱序
processItem(item).then((result) => results.add(result));
}
}
// ✅ 保证顺序的异步处理
Future<void> processDataCorrectly(List<int> data) async {
List<String> orderedResults = [];
for (int item in data) {
String result = await processItem(item);
orderedResults.add(result);
}
results.addAll(orderedResults);
}
// ✅ 并行处理但保持顺序
Future<void> processDataParallel(List<int> data) async {
List<String> parallelResults = await Future.wait(
data.map((item) => processItem(item))
);
results.addAll(parallelResults);
}
8. 固定长度List的误用
// ❌ 试图修改固定长度List
List<int> fixedList = List.filled(5, 0, growable: false);
try {
fixedList.add(6); // UnsupportedError
fixedList.removeAt(0); // UnsupportedError
fixedList.clear(); // UnsupportedError
} catch (e) {
print('固定长度List错误: $e');
}
// ✅ 检查List是否可增长
extension ListExtensions<T> on List<T> {
bool get isGrowable {
try {
add(first);
removeLast();
return true;
} catch (e) {
return false;
}
}
}
// ✅ 安全的操作
if (fixedList.isGrowable) {
fixedList.add(6);
} else {
print('List是固定长度的');
}
最佳实践
1. 类型安全
// 推荐:明确指定类型
List<String> names = <String>[];
// 避免:使用dynamic
List<dynamic> badList = [];
2. 空安全
// 处理可能为空的List
List<String>? nullableList;
// 安全访问
if (nullableList != null && nullableList.isNotEmpty) {
print(nullableList.first);
}
// 使用null-aware操作符
print(nullableList?.length ?? 0);
3. 不可变性
// 创建不可变List
List<int> immutableList = List.unmodifiable([1, 2, 3]);
// 或使用const
const List<String> constantList = ['a', 'b', 'c'];
4. 内存管理
// 及时清理大型List
void processLargeData() {
List<String> largeList = generateLargeData();
// 处理数据
processData(largeList);
// 清理内存
largeList.clear();
largeList = null;
}
5. 错误处理
// 安全的元素访问
T? safeGet<T>(List<T> list, int index) {
if (index >= 0 && index < list.length) {
return list[index];
}
return null;
}
// 使用try-catch处理可能的异常
try {
var element = list.single; // 当list不是单元素时会抛出异常
} catch (e) {
print('Error: $e');
}
总结
Dart的List类提供了丰富而强大的API,支持各种常见的集合操作。在使用时应该注意:
- 选择合适的创建方式:根据使用场景选择字面量、构造函数或工厂方法
- 注意性能影响:了解各种操作的时间复杂度,选择高效的算法
- 保证类型安全:使用泛型确保类型安全
- 处理边界情况:检查索引越界和空值情况
- 合理使用函数式方法:map、where、fold等方法可以让代码更简洁
通过掌握这些API和最佳实践,可以更高效地使用Dart中的List类型。
本文档基于Dart 3.x版本整理,如有更新请参考官方文档。