Dart List API 详细总结

89 阅读16分钟

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) - 是否可增长

构造函数使用注意事项

⚠️ 重要提醒:

  1. 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); // 正常工作
    
  2. 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], [], []]
    
  3. 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); // 编译错误!
    
    
  4. 生成器函数的性能考虑

    // ❌ 避免:在生成器中执行昂贵操作
    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异常

访问和修改使用注意事项

⚠️ 重要提醒:

  1. 索引越界检查

    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而不是异常
    
  2. 负数索引处理

    List<int> numbers = [1, 2, 3, 4, 5];
    
    // ❌ Dart不支持负数索引(不像Python)
    // print(numbers[-1]); // 抛出RangeError异常
    
    // ✅ 正确的最后元素访问方式
    print(numbers[numbers.length - 1]); // 5
    print(numbers.last); // 5
    
  3. 空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为空');
    }
    
  4. 修改固定长度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]

添加元素使用注意事项

⚠️ 重要提醒:

  1. 固定长度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]);
    
  2. insert操作的性能影响

    List<int> numbers = [1, 2, 3, 4, 5];
    
    // ❌ 在开头插入效率低(O(n)时间复杂度)
    numbers.insert(0, 0); // 需要移动所有现有元素
    
    // ✅ 在末尾添加效率高(O(1)时间复杂度)
    numbers.add(6); // 直接添加到末尾
    
  3. 批量添加的性能优势

    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);
    
  4. 空值和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); // []

删除元素使用注意事项

⚠️ 重要提醒:

  1. 固定长度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();
    
  2. 删除操作中的索引变化陷阱

    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);
    
  3. removeLast在空List上的异常

    List<String> emptyList = [];
    
    // ❌ 空List调用removeLast会抛出StateError
    // String last = emptyList.removeLast(); // 异常!
    
    // ✅ 安全的删除方式
    if (emptyList.isNotEmpty) {
      String last = emptyList.removeLast();
    }
    
  4. 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)但避免了查找
    }
    
  5. 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

查找和检查使用注意事项

⚠️ 重要提醒:

  1. 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,同一个引用
    
  2. 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)时间复杂度
    
  3. 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;
    });
    
  4. 空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
    
  5. 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}

转换和映射使用注意事项

⚠️ 重要提醒:

  1. 惰性求值的陷阱

    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);  // 不会重复执行
    
  2. 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++;
      // 处理逻辑
    });
    
  3. 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();
    
  4. 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);
    }
    
  5. 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();
    
  6. 类型转换的安全性

    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);

高级操作使用注意事项

⚠️ 重要提醒:

  1. 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等惰性组件
    
  2. 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);
    }
    
  3. replaceRange的性能影响

    List<int> numbers = List.generate(10000, (i) => i);
    
    // ❌ 在列表前部替换会导致大量元素移动
    numbers.replaceRange(0, 100, [999]); // O(n)操作,移动9900个元素
    
    // ✅ 在列表后部操作效率更高
    numbers.replaceRange(9900, 10000, [999]); // 只移动少量元素
    
  4. 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]; // 每个位置都是独立的对象
    }
    
  5. 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
    
  6. 异步操作的并发控制

    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);
    }
    
  7. 异步操作的错误处理

    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是否可增长,默认falseList<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()转换为SetSet<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,支持各种常见的集合操作。在使用时应该注意:

  1. 选择合适的创建方式:根据使用场景选择字面量、构造函数或工厂方法
  2. 注意性能影响:了解各种操作的时间复杂度,选择高效的算法
  3. 保证类型安全:使用泛型确保类型安全
  4. 处理边界情况:检查索引越界和空值情况
  5. 合理使用函数式方法:map、where、fold等方法可以让代码更简洁

通过掌握这些API和最佳实践,可以更高效地使用Dart中的List类型。


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