文章

sublist

sublist

sublist

sublist

1. 方法介绍

sublist() 是 Dart 中 List 类的一个实例方法,用于从原列表中提取指定范围的元素,返回一个新的列表。

方法签名:

dart

List sublist(int start, [int? end])</code>

2. 参数说明

参数类型必需描述
startint起始索引(包含)
endint?结束索引(不包含),默认为列表长度

3. 基本用法

3.1 只指定起始位置

1
2
3
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8];
List<int> sublist1 = numbers.sublist(2);
print(sublist1); // [3, 4, 5, 6, 7, 8]

3.2 指定起始和结束位置

1
2
3
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8];
List<int> sublist2 = numbers.sublist(2, 5);
print(sublist2); // [3, 4, 5]

4. 完整示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void main() {
  // 示例列表
  List<String> fruits = ['apple', 'banana', 'orange', 'grape', 'mango', 'kiwi'];

  print('原始列表: $fruits');
  print('列表长度: ${fruits.length}');
  print('---');

  // 1. 从指定位置到末尾
  List<String> fromIndex2 = fruits.sublist(2);
  print('从索引2开始: $fromIndex2'); // [orange, grape, mango, kiwi]

  // 2. 指定范围
  List<String> range2to4 = fruits.sublist(2, 4);
  print('索引2到4(不包含4): $range2to4'); // [orange, grape]

  // 3. 获取前几个元素
  List<String> firstThree = fruits.sublist(0, 3);
  print('前三个元素: $firstThree'); // [apple, banana, orange]

  // 4. 获取最后几个元素
  List<String> lastThree = fruits.sublist(fruits.length - 3);
  print('最后三个元素: $lastThree'); // [grape, mango, kiwi]

  // 5. 获取中间部分
  List<String> middle = fruits.sublist(1, fruits.length - 1);
  print('去掉首尾: $middle'); // [banana, orange, grape, mango]

  // 6. 复制整个列表
  List<String> copy = fruits.sublist(0);
  print('完整副本: $copy'); // [apple, banana, orange, grape, mango, kiwi]

  // 7. 单个元素
  List<String> single = fruits.sublist(3, 4);
  print('单个元素: $single'); // [grape]
}

5. 边界情况处理

5.1 处理可能越界的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void safeSublistExamples() {
  List<int> numbers = [1, 2, 3];

  // 安全的子列表获取函数
  List<T> safeSublist<T>(List<T> list, int start, [int? end]) {
    end ??= list.length;

    // 确保索引在有效范围内
    start = start.clamp(0, list.length);
    end = end.clamp(0, list.length);

    // 如果起始位置大于等于结束位置,返回空列表
    if (start >= end) return [];

    return list.sublist(start, end);
  }

  print('安全获取示例:');
  print('正常范围: ${safeSublist(numbers, 1, 3)}'); // [2, 3]
  print('起始越界: ${safeSublist(numbers, 5, 10)}'); // []
  print('结束越界: ${safeSublist(numbers, 1, 10)}'); // [2, 3]
  print('起始>结束: ${safeSublist(numbers, 2, 1)}'); // []
}

5.2 实际应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void practicalExamples() {
  // 场景1: 分页功能
  List<String> allItems = List.generate(100, (index) => 'Item ${index + 1}');

  List<String> getPageItems(List<String> items, int page, int pageSize) {
    int start = (page - 1) * pageSize;
    int end = start + pageSize;

    // 防止越界
    if (start >= items.length) return [];
    if (end > items.length) end = items.length;

    return items.sublist(start, end);
  }

  print('分页示例 - 第2页,每页10条:');
  print(getPageItems(allItems, 2, 10));

  // 场景2: 时间序列数据截取
  List<DateTime> dates = [
    DateTime(2024, 1, 1),
    DateTime(2024, 1, 2),
    DateTime(2024, 1, 3),
    DateTime(2024, 1, 4),
    DateTime(2024, 1, 5),
  ];

  // 获取最近3天的数据
  List<DateTime> recentDates = dates.sublist(dates.length - 3);
  print('最近3天: $recentDates');

  // 场景3: 数据批处理
  List<int> bigData = List.generate(1000, (index) => index);

  void processInBatches(List<int> data, int batchSize) {
    for (int i = 0; i < data.length; i += batchSize) {
      int end = (i + batchSize < data.length) ? i + batchSize : data.length;
      List<int> batch = data.sublist(i, end);

      print('处理批次 ${i ~/ batchSize + 1}: ${batch.length} 条数据');
      // 实际处理逻辑...
    }
  }

  processInBatches(bigData, 100);
}

6. 注意事项

6.1 索引范围验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void indexValidation() {
  List<int> numbers = [1, 2, 3, 4, 5];

  try {
    // 这些会抛出 RangeError
    // List<int> error1 = numbers.sublist(-1);      // 起始索引为负
    // List<int> error2 = numbers.sublist(10);     // 起始索引超出范围
    // List<int> error3 = numbers.sublist(2, 10);  // 结束索引超出范围
    // List<int> error4 = numbers.sublist(3, 2);   // 起始索引大于结束索引

    // 安全的使用方式
    int start = 2;
    int end = 5;

    if (start >= 0 && end <= numbers.length && start <= end) {
      List<int> safe = numbers.sublist(start, end);
      print('安全获取: $safe');
    }
  } catch (e) {
    print('错误: $e');
  }
}

6.2 与其他方法的比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void methodComparison() {
  List<int> numbers = [1, 2, 3, 4, 5];

  // sublist vs getRange
  List<int> sublistResult = numbers.sublist(1, 4);        // [2, 3, 4]
  Iterable<int> rangeResult = numbers.getRange(1, 4);     // (2, 3, 4)
  List<int> rangeToList = numbers.getRange(1, 4).toList(); // [2, 3, 4]

  print('sublist: $sublistResult');
  print('getRange: $rangeResult');
  print('getRange.toList(): $rangeToList');

  // sublist 返回新的 List,getRange 返回 Iterable
}

7. 性能考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void performanceConsideration() {
  // sublist 的时间复杂度是 O(k),其中 k 是子列表的长度
  // 对于大型列表,sublist 是高效的

  List<int> largeList = List.generate(1000000, (index) => index);

  // 高效:只复制需要的部分
  Stopwatch stopwatch = Stopwatch()..start();
  List<int> sublist = largeList.sublist(500000, 500100);
  stopwatch.stop();

  print('获取100个元素的子列表耗时: ${stopwatch.elapsedMicroseconds} 微秒');

  // 对比:使用 where 过滤(可能更慢)
  stopwatch..reset()..start();
  List<int> filtered = largeList.where((item) => item >= 500000 && item < 500100).toList();
  stopwatch.stop();

  print('使用where过滤耗时: ${stopwatch.elapsedMicroseconds} 微秒');
}

总结

sublist() 方法是处理列表切片的最常用和最高效的方式之一。记住:

  • 起始索引包含,结束索引不包含
  • 始终要处理边界情况,避免 RangeError
  • 对于大型列表,sublist 比使用 where 过滤更高效
  • 返回的是新列表,对原列表的修改不会影响子列表(除非元素是可变对象)
本文由作者按照 CC BY 4.0 进行授权