现在位置: 首页 > Dart 教程 > 正文

Dart 集合类型

集合是用来存放多个数据的数据结构。

Dart 内置了三种核心集合类型:List(列表)、Set(集合)和 Map(映射),本章将逐一介绍它们的用法。


List:有序列表

List 是最常用的集合类型,用于存放一组有序的元素,允许重复。

你可以通过索引(从 0 开始)来访问 List 中的任意元素。

创建 List

实例

void main() {
  // 使用字面量创建 List
  List<String> sites = ['RUNOOB', 'Google', 'GitHub'];
  print('网站列表: $sites');

  // 类型推断(省略泛型)
  var numbers = [1, 2, 3, 4, 5];
  print('数字列表: $numbers');

  // 创建空列表
  var emptyList = <String>[];

  // 使用 List.filled 创建固定长度的列表
  var zeros = List.filled(3, 0);  // [0, 0, 0]
  print('填充列表: $zeros');

  // 使用 List.generate 生成列表
  var squares = List.generate(5, (i) => i * i);
  print('平方数列表: $squares');
}
网站列表: [RUNOOB, Google, GitHub]
数字列表: [1, 2, 3, 4, 5]
填充列表: [0, 0, 0]
平方数列表: [0, 1, 4, 9, 16]

访问和修改 List 元素

实例

void main() {
  var fruits = ['苹果', '香蕉', '橙子'];

  // 通过索引访问(从 0 开始)
  print('第一个水果: ${fruits[0]}');  // 苹果
  print('最后一个: ${fruits[fruits.length - 1]}');  // 橙子

  // 修改元素
  fruits[1] = '葡萄';
  print('修改后: $fruits');  // [苹果, 葡萄, 橙子]

  // 获取长度
  print('水果数量: ${fruits.length}');

  // 检查是否为空
  print('列表为空? ${fruits.isEmpty}');
  print('列表非空? ${fruits.isNotEmpty}');
}
第一个水果: 苹果
最后一个: 橙子
修改后: [苹果, 葡萄, 橙子]
水果数量: 3
列表为空? false
列表非空? true

List 常用操作

实例

void main() {
  var list = ['RUNOOB', 'Dart'];

  // 添加元素
  list.add('Flutter');           // 添加单个
  list.addAll(['Google', 'AI']);  // 添加多个
  print('添加后: $list');

  // 插入元素(在指定位置)
  list.insert(1, '教程');
  print('插入后: $list');

  // 删除元素
  list.remove('AI');             // 按值删除
  list.removeAt(0);              // 按索引删除
  list.removeLast();             // 删除最后一个
  print('删除后: $list');

  // 查找元素
  bool hasDart = list.contains('Dart');
  int index = list.indexOf('Dart');
  print('包含 Dart? $hasDart, 位置: $index');

  // 排序
  var nums = [3, 1, 4, 1, 5, 9];
  nums.sort();
  print('排序后: $nums');

  // 反转
  var reversed = nums.reversed.toList();
  print('反转后: $reversed');

  // 截取子列表
  var subList = nums.sublist(0, 3);
  print('前3个: $subList');
}
添加后: [RUNOOB, Dart, Flutter, Google, AI]
插入后: [RUNOOB, 教程, Dart, Flutter, Google, AI]
删除后: [教程, Dart, Flutter]
包含 Dart? true, 位置: 1
排序后: [1, 1, 3, 4, 5, 9]
反转后: [9, 5, 4, 3, 1, 1]
前3个: [1, 1, 3]

List 的函数式方法

Dart 的 List 支持 map、where、reduce 等函数式方法,让数据处理更加简洁。

实例

void main() {
  var scores = [55, 78, 92, 60, 45, 88];

  // where:过滤(保留满足条件的元素)
  var passed = scores.where((s) => s >= 60);
  print('及格分数: $passed');

  // map:映射(将每个元素转换为新值)
  var grades = scores.map((s) => s >= 60 ? '及格' : '不及格');
  print('评级: $grades');

  // where + map 链式调用
  var highScores = scores
      .where((s) => s >= 80)
      .map((s) => '高分: $s')
      .toList();
  print('高分列表: $highScores');

  // reduce:累积计算
  var total = scores.reduce((sum, s) => sum + s);
  print('RUNOOB 总分: $total');

  // fold:带初始值的累积计算
  var avg = scores.fold(0, (sum, s) => sum + s) / scores.length;
  print('平均分: ${avg.toStringAsFixed(1)}');

  // any / every:存在性判断
  bool hasFullMark = scores.any((s) => s == 100);
  bool allPassed = scores.every((s) => s >= 60);
  print('有满分吗? $hasFullMark');
  print('全部及格? $allPassed');
}
及格分数: (78, 92, 60, 88)
评级: (不及格, 及格, 及格, 及格, 不及格, 及格)
高分列表: [高分: 92, 高分: 88]
RUNOOB 总分: 418
平均分: 69.7
有满分吗? false
全部及格? false

map() 和 where() 返回的是 Iterable(惰性求值),需要用 toList() 转换为 List 才会真正执行计算。如果你只需要遍历一次,直接用 Iterable 即可,不需要转换。


Set:唯一元素集合

Set 和 List 类似,但 Set 中的每个元素只能出现一次,不允许重复。

Set 是无序的,不能通过索引访问元素。

实例

void main() {
  // 创建 Set(元素自动去重)
  Set&lt;String&gt; tags = {'Dart', 'Flutter', 'Dart', 'RUNOOB'};
  print('标签集合: $tags');  // {Dart, Flutter, RUNOOB},重复的 Dart 被去掉了
  print('标签数量: ${tags.length}');  // 3

  // 添加元素
  tags.add('Google');
  tags.add('Dart');  // Dart 已存在,不会重复添加
  print('添加后: $tags');

  // 删除元素
  tags.remove('Google');
  print('删除后: $tags');

  // 检查是否包含
  print('包含 RUNOOB? ${tags.contains('RUNOOB')}');

  // 集合运算
  var setA = {1, 2, 3, 4};
  var setB = {3, 4, 5, 6};

  print('交集: ${setA.intersection(setB)}');  // {3, 4}
  print('并集: ${setA.union(setB)}');          // {1, 2, 3, 4, 5, 6}
  print('差集: ${setA.difference(setB)}');     // {1, 2}
}
标签集合: {Dart, Flutter, RUNOOB}
标签数量: 3
添加后: {Dart, Flutter, RUNOOB, Google}
删除后: {Dart, Flutter, RUNOOB}
包含 RUNOOB? true
交集: {3, 4}
并集: {1, 2, 3, 4, 5, 6}
差集: {1, 2}

Set 的典型使用场景是"去重"——当你不需要重复元素时,用 Set 比用 List 手动去重要高效得多。


Map:键值对映射

Map 用于存储键值对(Key-Value Pair),每个键对应一个值。

键在 Map 中必须是唯一的,值可以重复。

创建和访问 Map

实例

void main() {
  // 使用字面量创建 Map
  Map&lt;String, String&gt; siteInfo = {
    'name': 'RUNOOB',
    'url': 'https://www.runoob.com',
    'type': '编程教程',
  };

  // 通过键访问值
  print('站点名称: ${siteInfo['name']}');
  print('站点 URL: ${siteInfo['url']}');

  // 访问不存在的键返回 null
  print('描述: ${siteInfo['description']}');  // null

  // 添加/修改键值对
  siteInfo['language'] = '中文';
  siteInfo['name'] = 'RUNOOB.COM';  // 修改已有键的值
  print('更新后: $siteInfo');

  // 获取所有键和所有值
  print('所有键: ${siteInfo.keys}');
  print('所有值: ${siteInfo.values}');

  // 检查键是否存在
  print('有 url 键? ${siteInfo.containsKey('url')}');
  print('有 desc 键? ${siteInfo.containsKey('desc')}');
}
站点名称: RUNOOB
站点 URL: https://www.runoob.com
描述: null
更新后: {name: RUNOOB.COM, url: https://www.runoob.com, type: 编程教程, language: 中文}
所有键: (name, url, type, language)
所有值: (RUNOOB.COM, https://www.runoob.com, 编程教程, 中文)
有 url 键? true
有 desc 键? false

Map 常用操作

实例

void main() {
  var scores = {
    'runoob': 95,
    'Alice': 87,
    'Bob': 72,
  };

  // 遍历 Map
  scores.forEach((name, score) {
    print('$name: $score 分');
  });

  // 删除键值对
  scores.remove('Bob');
  print('删除 Bob 后: $scores');

  // 获取值或默认值
  int aliceScore = scores['Alice'] ?? 0;
  int eveScore = scores['Eve'] ?? 0;  // 不存在,返回默认值 0
  print('Alice: $aliceScore, Eve: $eveScore');

  // putIfAbsent:键不存在时才添加
  scores.putIfAbsent('runoob', () => 100);  // 已存在,不添加
  scores.putIfAbsent('David', () => 80);    // 不存在,添加
  print('最终: $scores');

  // 获取长度
  print('人数: ${scores.length}');
}
runoob: 95 分
Alice: 87 分
Bob: 72 分
删除 Bob 后: {runoob: 95, Alice: 87}
Alice: 87, Eve: 0
最终: {runoob: 95, Alice: 87, David: 80}
人数: 3

Map 的键可以是任何类型(String、int 等),但必须保证键的相等性有意义。使用自定义对象作为键时,需要确保正确实现了 == 和 hashCode。


集合展开运算符 ... 与 ...?

展开运算符可以将一个集合的元素"展开"到另一个集合中。

实例

void main() {
  var basics = ['Dart', 'Flutter'];
  var advanced = ['异步编程', '状态管理'];

  // ... 展开运算符:将另一个集合的元素插入
  var allCourses = ['RUNOOB 入门', ...basics, ...advanced];
  print('所有课程: $allCourses');

  // ...? 空安全展开:如果集合为 null,则跳过
  List&lt;String&gt;? optionalList;  // 可能为 null
  var safeList = ['第一项', ...?optionalList];
  print('安全展开: $safeList');  // 只有第一项

  optionalList = ['额外内容'];
  safeList = ['第一项', ...?optionalList];
  print('有值展开: $safeList');
}
所有课程: [RUNOOB 入门, Dart, Flutter, 异步编程, 状态管理]
安全展开: [第一项]
有值展开: [第一项, 额外内容]

集合中的 if 和 for(集合控制流)

Dart 允许在集合字面量中直接使用 if 和 for,这是非常实用的语法糖。

实例

void main() {
  bool showAdmin = true;

  // 集合中的 if:根据条件决定是否包含元素
  var menuItems = [
    '首页',
    '教程',
    if (showAdmin) '管理后台',  // 条件为 true 时才加入
    '关于',
  ];
  print('菜单: $menuItems');

  // 集合中的 for:从另一个集合生成元素
  var numbers = [1, 2, 3];
  var doubled = [
    for (var n in numbers) n * 2,  // 遍历生成新元素
  ];
  print('翻倍: $doubled');

  // if + for 可以组合使用
  var tags = ['Dart', 'Flutter'];
  var links = [
    'RUNOOB 首页',
    for (var tag in tags) 'https://runoob.com/$tag',
  ];
  print('链接: $links');
}
菜单: [首页, 教程, 管理后台, 关于]
翻倍: [2, 4, 6]
链接: [RUNOOB 首页, https://runoob.com/Dart, https://runoob.com/Flutter]

三种集合对比

特性ListSetMap
有序性有序无序无序(但有迭代顺序)
允许重复允许不允许键不允许重复,值允许
索引访问支持 []不支持通过键访问 []
典型场景有序列表、序列去重、标签集合键值映射、配置项
创建字面量[]{}{key: value}

初学者常犯的错误:Set 和 Map 的字面量都用 {}。区别在于:Map 的字面量中有冒号(key: value),而 Set 没有。如果写 var x = {},Dart 会推断为 Map,不是 Set。要创建空 Set,必须写 var x = {}。