集合概述

Dart 中的集合数据类型(Collection)包括 List、Set、Map 和 Queue。

classDiagram
  Iterable~E~ <|.. List~E~
  Iterable~E~ <|.. Set~E~
  Iterable~E~ <|.. Queue~E~

  class Iterable{
    <<mixin>>
    + Iterator~E~ get iterator
  }
  <<interface>> List
  <<interface>> Set
  <<interface>> Queue

  Iterator~E~ <-- Iterable~E~
  Iterable <-- Map~K,V~

  class Iterator {
    <<interface>>
    + E get current
    + bool moveNext()
  }

  class Map{
    <<interface>>
    + Iterable~MapEntry&lt;K, V&gt;~ get entries
    + Iterable~K~ get keys
    + Iterable~V~ get values
  }

List

List 是一组有序的对象(元素)的集合,可以通过下标(从 0 开始的整数)获取对应位置的元素。

// ex621.dart
void main() {
  var letters = ['A', 'B', 'C', 'A'];
  assert(4 == letters.length);
  assert('A' == letters[0]);
  assert('B' == letters[1]);
  assert('C' == letters[2]);
  print(letters); // Output: [A, B, C, A]
  print(letters.toSet()); // Output: {A, B, C}
}

Set

Set 不关心元素的排序顺序,它关注的是元素是否在集合里,Set 中的元素不会重复。

// ex622.dart
void main() {
  var letters = {'A', 'B', 'C', 'A'};
  assert(3 == letters.length); // 1
  print(letters); // 2 Output: {A, B, C}
  print(letters.toList()); // 2a Output: [A, B, C]
}
  1. letters的长度为 3 而不是 4;
  2. letters包含的 3 个元素即 {A, B, C}A 只出现了一次。

Queue

Queue 表示队列,它提供了队列两端的添加/移除操作。

// ex623.dart
import 'dart:collection';

void main() {
  var q = Queue.of(['A', 'B', 'C']);
  print(q); // Output: {A, B, C}

  q.addFirst('X');
  print(q); // Output: {X, A, B, C}

  q.addLast('Y');
  print(q); // Output: {X, A, B, C, Y}

  q.removeLast();
  print(q); // Output: {X, A, B, C}

  q.removeFirst();
  print(q); // Output: {A, B, C}
}

Map

Map 是键值(key-value)对的集合,它表达了 key 到 value 的映射关系。

// ex624.dart
void main() {
  var prices = {'Apple': 1.2, 'Banana': 1.4, 'Cherry': 2.1};
  print(prices.runtimeType); // Output: _Map<String, double>
  print(prices['Apple']); // Output: 1.2

  prices['Apple'] = 1.5;
  prices['Damson'] = .5;
  print(prices); // Output: {Apple: 1.5, Banana: 1.4, Cherry: 2.1, Damson: 0.5}

  print(prices.keys); // Output: (Apple, Banana, Cherry, Damson)
  print(prices.values); // Output: (1.5, 1.4, 2.1, 0.5)
  print(prices.entries);
  // Output: (MapEntry(Apple: 1.5), MapEntry(Banana: 1.4), MapEntry(Cherry: 2.1), MapEntry(Damson: 0.5))
}

集合元素

先以 List 为例,简单说明下集合元素。在以[a, b, ...]这样的语法书写 List 时,其中的ab等称为元素(elements);每个元素将被求值(evaluated)以产生 0 到多个值(value),这些值随后将被插入到结果 List 中。集合元素分为叶元素(leaf elements)和控制流元素(control flow elements)两类。

// ex633.dart
void main() {
  print([1 * 1, 2 * 2, 3 * 3]); // 1 Output: [1, 4, 9]

  int? a;
  print([1, 2, a, 3]); // 2 Output: [1, 2, null, 3]
  print([1, 2, ?a, 3]); // 2a Output: [1, 2, 3]

  final arr = [3, 4];
  var arr2 = [1, 2, ...arr, 5]; // 3
  print('${arr2.runtimeType} $arr2'); // Output: List<int> [1, 2, 3, 4, 5]

  List<int>? arr3;
  print([1, 2, ...?arr3, 3]); // 4 Output: [1, 2, 3]
}
  1. 此行代码展示的是表达式元素(Expression elements),它属于叶元素;
  2. 这行中的 ?a 是 Null-aware 元素,请与上一行代码进行对比;
  3. 这行中的 ...arr 乃 Spread 元素;
  4. 这行中的 ...?arr3 乃 Null-aware Spread 元素。

if 元素

if元素 和 for元素 都是控制流元素。下面的示例展示了if元素的各种用法。

// ex634.dart
void main() {
  var present = true;
  print([0, if (present) 1, 2, 3]); // 1 Output: [0, 1, 2, 3]
  print([0, if (!present) 1, 2, 3]); // 1a Output: [0, 2, 3]

  var letter = 'A';
  print([
    if (letter == 'A') 'apple' else 'orange',
    'orange',
  ]); // 2 Output: [apple, orange]

  print([
    if (letter == 'A') 'apricot' else if (letter == 'O') 'orange',
    'orange',
  ]); // 3 Output: [apricot, orange]

  var data = ['apple', 0.2];
  print([
    if (data case [var name, var price])
      'The price of $name is \$$price'
    else
       'Error data',
  ]); // 4 Output: [The price of apple is $0.2]
}

for 元素

如下示例简单展示了for元素的用法。

// ex635.dart
void main() {
  var numbers = [for (var i = 2; i <= 8; i += 2) i, 10]; // 1
  print(numbers); // Output: [2, 4, 6, 8, 10]

  var squares = [for (var n in numbers) n * n]; // 2
  print(squares); // Output: [4, 16, 36, 64, 100]
}

嵌套控制流元素

控制流元素可以互相嵌套。

// ex636.dart
void main() {
  var evens = [
    for (var i = 0; i < 10; i++)
      if (i % 2 == 0) i,
  ];
  print(evens); // Output: [0, 2, 4, 6, 8]

  var numbers = [
    for (var i = 0; i < 10; i++)
      if (i % 3 == 0)
        for (var j = i; j < i + 3; j++)
          if (j < 10) j,
  ];
  print(numbers); // Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}