1. Dart列表基础概念与定义
在Dart语言中,列表(List)是最常用的集合类型之一,它类似于其他编程语言中的数组。列表是一个有序的集合,可以存储多个相同或不同类型的元素。与固定长度的数组不同,Dart的列表是可变长度的,这意味着我们可以在运行时动态地添加或删除元素。
1.1 列表的基本定义方式
定义列表最简单的方式是使用方括号[]语法:
dart复制void main() {
// 定义一个字符串列表
List<String> studentArray = ["zhangsan", "lisi", "wangwu"];
print(studentArray); // 输出: [zhangsan, lisi, wangwu]
// 定义混合类型列表
var mixedList = [1, "two", 3.0, true];
print(mixedList); // 输出: [1, two, 3.0, true]
}
注意:虽然Dart支持混合类型列表,但在Flutter开发中,为了类型安全和代码可维护性,建议尽量使用单一类型的列表。可以使用泛型
List<T>来指定列表元素的类型。
1.2 列表的类型推断与泛型
Dart的类型推断系统很强大,我们可以使用var关键字让编译器自动推断列表类型:
dart复制var numbers = [1, 2, 3]; // 推断为List<int>
var names = ['Alice', 'Bob']; // 推断为List<String>
为了代码的清晰性和类型安全,显式声明列表类型是个好习惯:
dart复制List<int> ages = [20, 25, 30];
List<bool> isActiveList = [true, false, true];
1.3 空列表与固定长度列表
创建空列表的几种方式:
dart复制var emptyList1 = [];
var emptyList2 = List.empty(); // 创建一个不可变的空列表
var emptyList3 = List.empty(growable: true); // 创建一个可扩展的空列表
创建固定长度列表:
dart复制var fixedList = List.filled(3, ''); // 创建包含3个空字符串的列表
fixedList[0] = 'first'; // 可以修改元素
// fixedList.add('new'); // 会抛出异常,因为长度固定
2. 列表的基本操作
2.1 访问列表元素
访问列表元素使用下标运算符[],索引从0开始:
dart复制var fruits = ['apple', 'banana', 'orange'];
print(fruits[0]); // 输出: apple
print(fruits[2]); // 输出: orange
// print(fruits[3]); // 抛出RangeError异常
安全访问元素的几种方式:
dart复制// 使用length属性检查
if (fruits.length > 3) {
print(fruits[3]);
}
// 使用try-catch
try {
print(fruits[3]);
} catch (e) {
print('索引越界: $e');
}
// 使用getter方法安全获取
print(fruits.elementAtOrNull(3)); // 输出: null
2.2 修改列表元素
直接通过索引修改元素:
dart复制fruits[1] = 'pear';
print(fruits); // 输出: [apple, pear, orange]
替换多个元素:
dart复制fruits.replaceRange(0, 2, ['grape', 'kiwi']);
print(fruits); // 输出: [grape, kiwi, orange]
2.3 列表的遍历
基本的for循环遍历:
dart复制for (var i = 0; i < fruits.length; i++) {
print('${i + 1}. ${fruits[i]}');
}
for-in循环:
dart复制for (var fruit in fruits) {
print(fruit);
}
forEach方法:
dart复制fruits.forEach((fruit) {
print(fruit);
});
3. 列表的增删操作
3.1 添加元素
添加单个元素到列表末尾:
dart复制var numbers = [1, 2, 3];
numbers.add(4);
print(numbers); // 输出: [1, 2, 3, 4]
添加多个元素:
dart复制numbers.addAll([5, 6]);
print(numbers); // 输出: [1, 2, 3, 4, 5, 6]
在指定位置插入元素:
dart复制numbers.insert(0, 0); // 在索引0处插入0
print(numbers); // 输出: [0, 1, 2, 3, 4, 5, 6]
numbers.insertAll(3, [3.1, 3.2]); // 在索引3处插入多个元素
print(numbers); // 输出: [0, 1, 2, 3.1, 3.2, 3, 4, 5, 6]
3.2 删除元素
删除指定值的第一个出现:
dart复制var letters = ['a', 'b', 'c', 'b', 'd'];
letters.remove('b');
print(letters); // 输出: [a, c, b, d]
删除指定位置的元素:
dart复制letters.removeAt(2);
print(letters); // 输出: [a, c, d]
删除最后一个元素:
dart复制letters.removeLast();
print(letters); // 输出: [a, c]
删除范围内的元素:
dart复制var nums = [1, 2, 3, 4, 5, 6];
nums.removeRange(1, 4); // 删除索引1到3的元素(左闭右开)
print(nums); // 输出: [1, 5, 6]
删除满足条件的元素:
dart复制nums.removeWhere((num) => num > 3);
print(nums); // 输出: [1]
保留满足条件的元素:
dart复制nums.retainWhere((num) => num.isEven);
print(nums); // 输出: [] (因为1不是偶数)
4. 列表的高级操作
4.1 列表的查询与过滤
使用where方法过滤元素:
dart复制var numbers = [1, 2, 3, 4, 5, 6];
var evenNumbers = numbers.where((n) => n % 2 == 0).toList();
print(evenNumbers); // 输出: [2, 4, 6]
使用firstWhere和lastWhere查找元素:
dart复制var firstEven = numbers.firstWhere((n) => n % 2 == 0);
print(firstEven); // 输出: 2
var lastUnder5 = numbers.lastWhere((n) => n < 5);
print(lastUnder5); // 输出: 4
使用every和any检查条件:
dart复制var allPositive = numbers.every((n) => n > 0);
print(allPositive); // 输出: true
var hasEven = numbers.any((n) => n % 2 == 0);
print(hasEven); // 输出: true
4.2 列表的转换
使用map方法转换元素:
dart复制var squared = numbers.map((n) => n * n).toList();
print(squared); // 输出: [1, 4, 9, 16, 25, 36]
使用expand方法展开列表:
dart复制var nested = [[1, 2], [3, 4]];
var flattened = nested.expand((list) => list).toList();
print(flattened); // 输出: [1, 2, 3, 4]
4.3 列表的排序
使用sort方法排序:
dart复制var randomNumbers = [3, 1, 4, 1, 5, 9, 2];
randomNumbers.sort();
print(randomNumbers); // 输出: [1, 1, 2, 3, 4, 5, 9]
自定义排序:
dart复制var words = ['apple', 'banana', 'cherry'];
words.sort((a, b) => a.length.compareTo(b.length));
print(words); // 输出: [apple, cherry, banana]
反转列表:
dart复制var reversed = List.from(randomNumbers.reversed);
print(reversed); // 输出: [9, 5, 4, 3, 2, 1, 1]
5. 列表的实用属性和方法
5.1 常用属性
dart复制var colors = ['red', 'green', 'blue'];
print(colors.length); // 输出: 3
print(colors.first); // 输出: red
print(colors.last); // 输出: blue
print(colors.isEmpty); // 输出: false
print(colors.isNotEmpty); // 输出: true
5.2 列表的拼接与分割
拼接列表:
dart复制var list1 = [1, 2];
var list2 = [3, 4];
var combined = [...list1, ...list2];
print(combined); // 输出: [1, 2, 3, 4]
分割列表:
dart复制var numbers = [1, 2, 3, 4, 5];
var sublist = numbers.sublist(1, 4);
print(sublist); // 输出: [2, 3, 4]
5.3 列表的填充与生成
使用generate方法生成列表:
dart复制var squares = List.generate(5, (i) => i * i);
print(squares); // 输出: [0, 1, 4, 9, 16]
填充列表:
dart复制var filled = List.filled(3, 'default');
print(filled); // 输出: [default, default, default]
6. 实际应用中的注意事项
6.1 性能考虑
-
添加元素:
add操作通常是O(1)时间复杂度,但当列表需要扩容时会是O(n)。如果预先知道列表大小,可以使用List.filled或提前设置足够容量。 -
插入元素:
insert操作是O(n)时间复杂度,因为需要移动后面的所有元素。在列表开头插入比在末尾插入代价更高。 -
删除元素:
remove、removeAt等操作也是O(n)时间复杂度,原因同上。 -
查找元素:
contains、indexOf等操作需要遍历列表,是O(n)时间复杂度。如果需要频繁查找,考虑使用Set。
6.2 不可变列表
在某些情况下,我们可能需要创建不可变的列表:
dart复制var immutableList = List.unmodifiable([1, 2, 3]);
// immutableList.add(4); // 会抛出UnsupportedError
6.3 列表的深拷贝
直接赋值或使用=操作符只是创建了列表的引用,要创建真正的副本:
dart复制var original = [1, 2, 3];
var copy1 = List.from(original); // 浅拷贝
var copy2 = [...original]; // 展开运算符,浅拷贝
var copy3 = original.toList(); // 浅拷贝
// 对于包含对象的列表,需要深拷贝
var deepCopy = original.map((e) => e).toList();
6.4 常见错误与调试技巧
-
索引越界:总是检查索引是否有效
dart复制// 错误写法 // print(list[list.length]); // 抛出RangeError // 正确写法 if (index >= 0 && index < list.length) { print(list[index]); } -
修改不可变列表:注意列表是否可修改
dart复制var fixed = List.filled(3, 0); // fixed.add(1); // 抛出UnsupportedError -
类型错误:注意泛型类型约束
dart复制List<int> numbers = [1, 2, 3]; // numbers.add('4'); // 编译错误 -
空安全:Dart的空安全特性
dart复制List<int>? nullableList; // print(nullableList.length); // 编译错误 print(nullableList?.length ?? 0); // 安全访问
7. Flutter中的列表应用实例
7.1 在Widget中使用列表
ListView是Flutter中常用的滚动列表组件:
dart复制ListView.builder(
itemCount: studentArray.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(studentArray[index]),
);
},
)
7.2 列表数据与状态管理
在Flutter中,当列表数据变化时需要更新UI:
dart复制class StudentList extends StatefulWidget {
@override
_StudentListState createState() => _StudentListState();
}
class _StudentListState extends State<StudentList> {
List<String> students = ["zhangsan", "lisi", "wangwu"];
void addStudent(String name) {
setState(() {
students.add(name);
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () => addStudent('New Student'),
child: Text('Add Student'),
),
ListView.builder(
shrinkWrap: true,
itemCount: students.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(students[index]),
);
},
),
],
);
}
}
7.3 列表性能优化
对于长列表,使用ListView.builder而不是ListView.children:
dart复制// 不推荐 - 会一次性创建所有子Widget
ListView(
children: students.map((s) => ListTile(title: Text(s))).toList(),
)
// 推荐 - 只创建可见的子Widget
ListView.builder(
itemCount: students.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(students[index]),
);
},
)
8. Dart列表与其他集合类型的比较
8.1 列表 vs Set
Set是无序且元素唯一的集合:
dart复制var list = [1, 2, 2, 3];
var set = {1, 2, 2, 3}; // 实际存储{1, 2, 3}
print(list); // [1, 2, 2, 3]
print(set); // {1, 2, 3}
8.2 列表 vs Map
Map是键值对集合:
dart复制var list = ['a', 'b', 'c'];
var map = {0: 'a', 1: 'b', 2: 'c'};
print(list[1]); // b
print(map[1]); // b
8.3 如何选择合适的集合类型
- 需要有序且允许重复元素 → List
- 需要唯一元素且不关心顺序 → Set
- 需要通过键快速查找值 → Map
- 需要同时保持顺序和唯一性 → 可以使用LinkedHashSet
在实际Flutter开发中,列表是最常用的集合类型,特别是在构建动态UI时。但了解不同集合类型的特点有助于我们选择最合适的数据结构。