# Dart教程 - 6 容器

什么是容器?

容器就是可以存储多个数据的一种数据类型,容器内的每一个数据称之为元素,其中的元素可以是任意类型的数据,包括字符串、数字、布尔,甚至是容器等等。

例如有一个班级,有40个学生,我们肯定不能定义40个变量来存储学生的信息,有1000个人,那不要命了,我们可以将所有学生的信息放到容器中。

在Dart中常用的容器分为3类:

  • 列表(list)
  • 集合(set)
  • 字典(map)

不同的容器有不同的特点,例如:

  • 是否支持重复的元素,有的容器中的元素不能重复,有的可以。
  • 是否有序

下面一一介绍。

# 6.1 List

List 列表就是一个普通的容器,满足你最原始的想象,主要有如下特点:

  • 列表中的元素可以重复
  • 列表中的元素可以修改,可以增加、修改、删除
  • 列表中的元素是有序的,可以通过索引来访问
  • 列表中可以存储不同数据类型的数据

# 1 创建List

// 方式一:创建一个空列表
List<String> emptyList = [];

// 方式二:创建一个包含元素的列表
List<int> numbers = [1, 2, 3, 4, 5];
1
2
3
4
5

当然使用 var 来声明也是可以的。

var emptyList = [];
var emptyList = [1, 2, 3, 4, 5];
1
2

还可以通过来创建定长的List,定长的List长度是固定的,所以不能添加和删除元素,但是可以修改元素。

// 创建定长的列表
List<int> list = List.filled(5, 2); // 创建一个定长的List
print(list); 		//[2, 2, 2, 2, 2]
1
2
3

第一个参数5表示List的长度,第二个2表示所有的元素都是2。

# 2 基础操作

# 添加元素

var numbers = [];
numbers.add(1);
numbers.add(2);
numbers.add(3);
1
2
3
4

# 访问元素

可以使用下标来访问列表中的元素,从0开始,注意不要越界。

var numbers = [1, 2, 3];
print(numbers[0]); 				// 1
1
2

# 获取长度

var numbers = [1, 2, 3];
print(numbers.length); 		// 3
1
2

# 插入元素

var numbers = [1, 2, 3];
numbers.insert(1, 4);
print(numbers); 					// [1, 4, 2, 3]
1
2
3

也可以在执行位置插入数组:

numbers.insertAll(1, [4, 5]);	//[1, 4, 5, 2, 3]
1

# 删除元素

var numbers = [1, 2, 3];
numbers.remove(2);
print(numbers); // [1, 3]
1
2
3

也可以使用removeAt(index)函数删除指定index的元素。

# 清空List

var numbers = [1, 2, 3];
numbers.clear();
print(numbers); // []
1
2
3

# 3 遍历List

当遍历 Dart 中的列表时,您可以使用以下方法:

使用 for 循环

List<String> fruits = ['apple', 'banana', 'orange'];

for (int i = 0; i < fruits.length; i++) {
  print(fruits[i]);
}
1
2
3
4
5

for 循环中,使用列表的长度 fruits.length 作为循环条件,以便在每个索引位置上访问列表元素 fruits[i]

使用 for-in 循环

List<String> fruits = ['apple', 'banana', 'orange'];

for (var fruit in fruits) {
  print(fruit);
}
1
2
3
4
5

for-in 循环中,将每个列表元素 fruit 赋值给变量 var fruit,然后在循环体中执行相应的操作。这种方法比使用 for 循环更简洁,特别是当您不需要访问每个元素的索引时。

使用 forEach 方法

List<String> fruits = ['apple', 'banana', 'orange'];

fruits.forEach((fruit) {
  print(fruit);
});
1
2
3
4
5

forEach 方法中,您需要提供一个回调函数 (fruit) => { print(fruit) },该回调函数将在列表的每个元素上执行相应的操作。这种方法的优点是语法简洁,易于阅读。

使用迭代器

List<String> fruits = ['apple', 'banana', 'orange'];

Iterator<String> iterator = fruits.iterator;

while (iterator.moveNext()) {
  print(iterator.current);
}
1
2
3
4
5
6
7

在使用迭代器遍历列表时,首先需要获取列表的迭代器 fruits.iterator,然后可以使用 while 循环和迭代器的 moveNext()current 方法来访问列表的每个元素。

# 4 其他常用操作

# 判断List是否为空

可以通过isEmpty()方法判断List是否为空,通过isNotEmpty()方法判断List是否不为空。

var numbers = [];
if (numbers.isEmpty) {
  print("集合为空");
}

numbers = [1, 2, 3, 4, 5];
if (numbers.isNotEmpty) {
  print("集合不为空");
}
1
2
3
4
5
6
7
8
9

# 检查包含某个元素

var numbers = [1, 2, 3];
print(numbers.contains(2)); 			// true
1
2

# 将List中的元素拼接成字符串

使用joint()方法,可以将List中的元素,使用指定的连接符,拼接成一个字符串。

举个栗子:

var numbers = [1, 2, 3];
var numberString = numbers.join(', ');
print(numberString); 							//输出:1, 2, 3
1
2
3

# 将字符串分割成List

我们可以将一个字符串,使用指定的分隔符,将字符串分割成一个List。

举个栗子:

String str = "1,2,3,4,5";
List<String> list = str.split(",");
print(list);											//输出:[1, 2, 3, 4, 5]
1
2
3

# 使用List中的元素生成新的List

使用map方法,可以依次取出List中的元素,然后进行处理,得到一个新的List。

举个栗子:

将一个List中的元素乘以2得到一个新的List。

var numbers = [1, 2, 3];

var doubledNumbers = numbers.map((number) {
  return number * 2;
}).toList();

print(doubledNumbers); // [2, 4, 6]
1
2
3
4
5
6
7

map得到的结果使用toList()方法转换为List。

也可以使用lambda表达式来简写:var doubledNumbers = numbers.map((number) => number * 2).toList();

# 过滤List中的元素

使用 where 方法,可以使用指定的条件过滤List中的元素,组成一个新的List。

举个栗子:

var numbers = [1, 2, 3, 4, 5];

var evenNumbers = numbers.where((number) {
  return number % 2 == 0;
}).toList();

print(evenNumbers); // [2, 4]
1
2
3
4
5
6
7

传递给where()的函数的返回值是bool值,满足条件的元素会被加入到新的List中。

也可以使用lambda表达式来简写:var evenNumbers = numbers.where((number) => number % 2 == 0).toList();

# 判断List中的元素是否有元素满足指定条件

可以使用any()方法,判断List中是否有元素满足指定的条件。

举个栗子:

判断List中是否有偶数

var numbers = [1, 2, 3, 4, 5];

var f = numbers.any((number) {
  return number % 2 == 0;
});
print(f); // true
1
2
3
4
5
6

# 判断List中的元素是否都满足指定条件

any()方法是判断集合中是否有元素满足指定条件,every()方法是用来判断List中所有的元素都满足指定的条件。

举个栗子:

判断List中的元素是否都是偶数

var numbers = [1, 2, 3, 4, 5];

var f = numbers.every((number) {
  return number % 2 == 0;
});

print(f); // false
1
2
3
4
5
6
7

# 6.2 Set

集合Set与List的区别:Set是一种不允许重复元素的容器类型,而且Set是无序的。

集合更重要的是用来做一些运算,例如求两个容器的交集、并集、差集等。

集合最主要的特点:

  • 不支持重复的元素

  • 内容是无序的,不能下标来访问元素

  • 集合是可以被修改的

  • 可以存储不同的数据类型

# 1 创建Set

// 方式一:创建一个空Set
Set<String> emptySet = {};

// 方式二:创建一个包含元素的集合
Set<String> fruits = {'apple', 'banana', 'orange'};

// 方式三:使用Set的构造函数来创建一个空集合
Set<String> fruits = Set<String>();

// 方式四:使用其他List或Set来创建
Set<String> fruits = Set<String>.from(['apple', 'banana', 'orange']);
Set<String> fruits = Set<String>.from({'apple', 'banana', 'orange'});
1
2
3
4
5
6
7
8
9
10
11
12

当然使用 var 来声明也是可以的。

var emptySet = {};
1

# 2 基础操作

# 添加元素

var numbers = {};
numbers.add(1);
numbers.add(2);
numbers.add(3);
1
2
3
4

# 访问元素

因为Set是无序的,所以无法通过下标来访问Set中的元素,如果想访问Set中的元素,只能通过遍历的方式来获取Set中的元素。

# 获取长度

var numbers = {1, 2, 3};
print(numbers.length); // 3
1
2

# 删除元素

var numbers = {1, 2, 3};
numbers.remove(2);
print(numbers); // {1, 3}
1
2
3

因为Set是无序的,所以无法通过下标来删除元素。

# 清空Set

var numbers = {1, 2, 3};
numbers.clear();
print(numbers); // {}
1
2
3

# 将Set转换为List

使用toList()方法可以将Set转换为List。

var numbers = {1, 2, 3};
List numList = numbers.toList();
print(numList); // [1, 2, 3]
1
2
3

# 3 遍历Set

当遍历一个 Set 时,您也可以使用多种方式,包括使用 for 循环、forEach() 方法、for-in 循环以及使用迭代器。下面我们将逐一介绍这些方法。

使用 for-in 循环

Set<String> fruits = {'apple', 'banana', 'orange'};

for (String fruit in fruits) {
  print(fruit);
}
1
2
3
4
5

在这个示例中,我们使用 for-in 循环遍历了 Set 中的每个元素,并打印每个元素。

使用 forEach() 方法

forEach() 是 Dart 中 Set 类型的方法,它接受一个函数作为参数,并对 Set 中的每个元素都调用该函数。例如:

Set<String> fruits = {'apple', 'banana', 'orange'};

fruits.forEach((fruit) {
  print(fruit);
});
1
2
3
4
5

在这个示例中,我们使用 forEach() 方法遍历了 Set 中的每个元素,并打印每个元素。

使用迭代器

Set 类型也实现了 Iterable 接口,因此您也可以使用迭代器来遍历 Set 中的元素。例如:

Set<String> fruits = {'apple', 'banana', 'orange'};
Iterator<String> iterator = fruits.iterator;

while (iterator.moveNext()) {
  print(iterator.current);
}
1
2
3
4
5
6

在这个示例中,我们首先获取了 Set 的迭代器,并使用 while 循环和 moveNext() 方法遍历了 Set 中的每个元素,并使用 current 属性访问当前元素。

使用 for 循环

因为 Set 没有索引,所以您可以使用 Set.toList() 方法将 Set 转换为 List,并使用 List.length 属性确定 List 的长度。例如:

Set<String> fruits = {'apple', 'banana', 'orange'};
List<String> fruitsList = fruits.toList();

for (int i = 0; i < fruitsList.length; i++) {
  print(fruitsList[i]);
}
1
2
3
4
5
6

在这个示例中,我们将 Set 转换为 List,然后使用 for 循环遍历 List 中的每个元素,并打印每个元素。

# 4 其他常用操作

因为List和Set都是容器,所以很多List有的操作Set也是有的。

# 判断Set是否为空

可以通过isEmpty()方法判断Set是否为空,通过isNotEmpty()方法判断Set是否不为空。

Set<int> numbers = {};
if (numbers.isEmpty) {
  print("Set为空");
}

numbers = {1, 2, 3, 4, 5};
if (numbers.isNotEmpty) {
  print("Set不为空");
}
1
2
3
4
5
6
7
8
9

# 检查包含某个元素

var numbers = {1, 2, 3};
print(numbers.contains(2)); // true
1
2

# 将Set中的元素拼接为字符串:

var numbers = {1, 2, 3};
var numberString = numbers.join(', ');
print(numberString); // '1, 2, 3'
1
2
3

Dart的Set类提供了许多有用的操作,例如:并集、交集和差集。

# 使用Set中的元素生成新的Set

和List一样,使用map方法,可以依次取出Set中的元素,然后进行处理,得到一个新的Set。

举个栗子:

将一个Set中的元素乘以2得到一个新的Set。

var numbers = {1, 2, 3};
var squaredNumbers = numbers.map((number) => number * 2).toSet();
print(squaredNumbers); // {2, 4, 6}
1
2
3

同样的where()、any()、every()方法,对Set都是适用的。

# 并集

使用union()方法将两个Set合并为一个Set。下面是一个示例:

var set1 = {1, 2, 3};
var set2 = {3, 4, 5};
var unionSet = set1.union(set2);
print(unionSet); // {1, 2, 3, 4, 5}
1
2
3
4

# 交集

使用intersection()方法获取两个Set的交集。下面是一个示例:

var set1 = {1, 2, 3};
var set2 = {3, 4, 5};
var intersectionSet = set1.intersection(set2);
print(intersectionSet); // {3}
1
2
3
4

# 差集

使用difference()方法获取两个Set的差集。下面是一个示例:

var set1 = {1, 2, 3};
var set2 = {3, 4, 5};
var differenceSet = set1.difference(set2);
print(differenceSet); // {1, 2}
1
2
3
4

# 6.3 Map

Map是一种键值对的数据结构,其中每个键对应一个值。

例如有一份数据:

姓名 成绩
zhangsan 94
lisi 96
wangwu 91

使用列表、集合的方式存储上面的数据是很不方便的。

而使用Map存储就很适合,可以将姓名作为key,成绩作为value。

{
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}
1
2
3
4
5

这样可以很容易通过姓名key得到对应的成绩value。

# 1 创建Map

map同样是使用 花括号{} 来定义的,但是存储的元素是一个一个的键值对。

// 方式一:创建一个空Map
Map<String, String> map = {};

// 方式二:创建一个包含元素的Map
Map<String, int> map = {'zhangsan': 94, 'lisi': 96, 'wangwu': 91};

// 方式三:使用Map的构造函数来创建一个空集合
Map<String, String> map = Map<String, String>();
1
2
3
4
5
6
7
8

Map<String, String> map 其中 <String, String> 分别表示 keyvalue 对应的数据类型。

当然,我们可以使用 var 来定义:

var emptyMap = Map<String, dynamic>();
1

# 2 基础操作

# 添加元素

var numbers = {};
numbers['one'] = 1;
numbers['two'] = 2;
numbers['three'] = 3;
print(numbers);				// {one: 1, two: 2, three: 3}
1
2
3
4
5

# 访问元素

访问元素的时候,通过 key 来访问。

var numbers = {'one': 1, 'two': 2, 'three': 3};
print(numbers['two']); // 2
1
2

如果访问不存在的key,则得到结果为null。

# 获取长度

var numbers = {'one': 1, 'two': 2, 'three': 3};
print(numbers.length); // 3
1
2

# 删除元素

删除的时候,通过 key 来删除

var numbers = {'one': 1, 'two': 2, 'three': 3};
numbers.remove('two');
print(numbers); 			// {'one': 1, 'three': 3}
1
2
3

# 清空Map

var numbers = {'one': 1, 'two': 2, 'three': 3};
numbers.clear();
print(numbers); 			// {}
1
2
3

# 3 遍历操作

使用 for...in 循环

Map<String, int> studentMarks = {'Alice': 90, 'Bob': 80, 'Charlie': 85};

for (var entry in studentMarks.entries) {
  var key = entry.key;
  var value = entry.value;
  print('Student $key got $value marks');
}
1
2
3
4
5
6
7

这个示例使用了 for...in 循环遍历 Map,其中的 entries 属性返回一个键值对的迭代器,然后在循环中使用 keyvalue 属性访问键和值。

使用 forEach() 方法

Map<String, int> studentMarks = {'Alice': 90, 'Bob': 80, 'Charlie': 85};

studentMarks.forEach((key, value) {
  print('Student $key got $value marks');
});
1
2
3
4
5

这个示例使用了 forEach() 方法遍历 Map,其中的 lambda 表达式接收键和值,并在控制台打印出每个学生的分数。

使用 keysvalues 属性

Map<String, int> studentMarks = {'Alice': 90, 'Bob': 80, 'Charlie': 85};

for (var key in studentMarks.keys) {
  var value = studentMarks[key];
  print('Student $key got $value marks');
}

for (var value in studentMarks.values) {
  print('Student got $value marks');
}
1
2
3
4
5
6
7
8
9
10

这个示例使用了 keysvalues 属性分别遍历 Map 的键和值,然后在循环中使用 keyvalue 变量访问相应的键或值。

# 4 其他常用操作

# 判断Map是否为空

可以通过isEmpty()方法判断Map是否为空,通过isNotEmpty()方法判断Map是否不为空。

Map numbers = {};
if (numbers.isEmpty) {
  print("Map为空");
}

numbers = {'one': 1, 'two': 2, 'three': 3};
if (numbers.isNotEmpty) {
  print("Map不为空");
}
1
2
3
4
5
6
7
8
9

# 检查包含某个键

var numbers = {'one': 1, 'two': 2, 'three': 3};
print(numbers.containsKey('two')); // true
1
2

# 检查包含某个值

var numbers = {'one': 1, 'two': 2, 'three': 3};
print(numbers.containsValue(2)); // true
1
2

# 获取Map中的所有键

var numbers = {'one': 1, 'two': 2, 'three': 3};
var keys = numbers.keys.toList();
print(keys); // ['one', 'two', 'three']
1
2
3

# 将Map中的键值对拼接为字符串

var numbers = {'one': 1, 'two': 2, 'three': 3};
var numberString = numbers.entries.map((entry) => '${entry.key}: ${entry.value}').join(', ');
print(numberString); // 'one: 1, two: 2, three: 3'
1
2
3