跳到主要內容

集合

Dart 內建支援列表 (list)、集合 (set) 和對映 (map) 集合。要了解有關配置集合中包含的型別,請查閱泛型

列表

#

在幾乎所有程式語言中,最常見的集合可能是陣列,或稱有序物件組。在 Dart 中,陣列是 List 物件,因此大多數人直接稱它們為列表

Dart 列表字面量由用方括號 ([]) 括起來的逗號分隔的元素列表表示。每個元素通常是一個表示式。以下是一個簡單的 Dart 列表:

dart
var list = [1, 2, 3];

您可以在 Dart 集合字面量中的最後一項後新增逗號。這個尾隨逗號不會影響集合,但有助於防止複製貼上錯誤。

dart
var list = ['Car', 'Boat', 'Plane'];

列表使用基於零的索引,其中 0 是第一個元素的索引,list.length - 1 是最後一個元素的索引。您可以使用 .length 屬性獲取列表的長度,並使用下標運算子 ([]) 訪問列表的元素。

dart
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);

要建立編譯時常量列表,請在列表字面量前新增 const

dart
var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.

有關列表的更多資訊,請參閱 dart:core 文件中的列表部分。

集合

#

Dart 中的集合 (set) 是唯一元素的無序集合。Dart 透過集合字面量和 Set 型別提供對集合的支援。

以下是一個使用集合字面量建立的簡單 Dart 集合:

dart
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

要建立一個空集合,請使用 {} 並在其前加上型別引數,或將 {} 賦值給 Set 型別的變數。

dart
var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.

使用 add()addAll() 方法向現有集合新增項:

dart
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);

使用 .length 獲取集合中的項數:

dart
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);

要建立編譯時常量集合,請在集合字面量前新增 const

dart
final constantSet = const {
  'fluorine',
  'chlorine',
  'bromine',
  'iodine',
  'astatine',
};
// constantSet.add('helium'); // This line will cause an error.

有關集合的更多資訊,請參閱 dart:core 文件中的集合部分。

對映

#

在對映中,每個元素都是一個鍵值對。鍵值對中的每個鍵都與一個值關聯,並且鍵和值都可以是任何型別的物件。每個鍵只能出現一次,儘管相同的值可以與多個不同的鍵關聯。Dart 透過對映字面量和 Map 型別提供對對映的支援。

以下是幾個使用對映字面量建立的簡單 Dart 對映:

dart
var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings',
};

var nobleGases = {2: 'helium', 10: 'neon', 18: 'argon'};

您可以使用對映建構函式建立相同的物件:

dart
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

使用下標賦值運算子 ([]=) 向現有對映新增新的鍵值對:

dart
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // Add a key-value pair

使用下標運算子 ([]) 從對映中檢索值:

dart
var gifts = {'first': 'partridge'};
assert(gifts['first'] == 'partridge');

如果您查詢對映中不存在的鍵,將返回 null

dart
var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);

使用 .length 獲取對映中的鍵值對數量:

dart
var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

要建立編譯時常量對映,請在對映字面量前新增 const

dart
final constantMap = const {2: 'helium', 10: 'neon', 18: 'argon'};

// constantMap[2] = 'Helium'; // This line will cause an error.

有關對映的更多資訊,請參閱 dart:core 文件中的對映部分。

集合元素

#

集合字面量包含一系列元素。在執行時,每個元素都會被評估,產生零個或多個值,然後這些值被插入到結果集合中。這些元素分為兩大類:葉元素和控制流元素。

  • 葉元素:將單個項插入到集合字面量中。

    • 表示式元素:評估單個表示式並將結果值插入到集合中。

    • 對映條目元素:評估一對鍵和值表示式並將結果條目插入到集合中。

  • 控制流元素:有條件地或迭代地向周圍集合新增零個或多個值。

    • 空感知元素:評估一個表示式,如果結果不為 null,則將該值插入到周圍集合中。

    • 展開元素:迭代給定序列(集合表示式)並將所有結果值插入到周圍集合中。

    • 空感知展開元素:與展開元素類似,但允許集合為 null,如果為 null 則不插入任何內容。

    • If 元素:根據給定的條件表示式有條件地評估一個內部元素,如果條件為假,則可選地評估另一個 else 元素。

    • For 元素:迭代並重複評估給定的內部元素,插入零個或多個結果值。

要了解有關集合元素的更多資訊,請參閱以下部分。

表示式元素

#

表示式元素評估單個表示式並將結果值插入到集合中。此表示式可以包含各種構造,如字面量、變數、運算子、函式呼叫和建構函式呼叫。

表示式元素在集合中具有以下語法:

dart
<expression>

對映條目元素

#

對映條目元素評估一對鍵和值表示式並將結果條目插入到集合中。該對中的鍵和值都可以是表示式。

對映條目元素在集合中具有以下語法:

dart
<key_expression>: <value_expression>

空感知元素

#

空感知元素評估一個表示式,如果結果不為 null,則將該值插入到周圍集合中。

空感知元素在表示式元素中具有以下語法:

dart
?<expression>

空感知元素在對映條目元素中具有以下語法:

dart
// key is a null-aware element
?<key_expression>: <value_expression>
dart
// value is a null-aware element
<key_expression>: ?<value_expression>
dart
// key and value are null-aware elements
?<key_expression>: ?<value_expression>

在以下示例中,空感知元素 ?a 的結果不會新增到名為 items 的列表中,因為 anull

dart
int? absentValue = null;
int? presentValue = 3;
var items = [
  1,
  ?absentValue,
  ?presentValue,
  absentValue,
  5,
]; // [1, 3, null, 5]

以下示例說明了在對映條目元素內部使用空感知元素的各種方式:

dart
String? presentKey = 'Apple';
String? absentKey = null;

int? presentValue = 3;
int? absentValue = null;

var itemsA = {presentKey: absentValue}; // {Apple: null}
var itemsB = {presentKey: ?absentValue}; // {}

var itemsC = {absentKey: presentValue}; // {null: 3}
var itemsD = {?absentKey: presentValue}; // {}

var itemsE = {absentKey: absentValue}; // {null: null}
var itemsF = {?absentKey: ?absentValue}; // {}

展開元素

#

展開元素迭代給定序列並將所有結果值插入到周圍集合中。

展開元素在集合中具有以下語法。序列表達式可以表示評估為實現 Iterable 介面的物件的任何表示式。

dart
...<sequence_expression>

在以下示例中,名為 a 的列表中的元素被新增到名為 items 的列表中。

dart
var a = [1, 2, null, 4];
var items = [0, ...a, 5]; // [0, 1, 2, null, 4, 5]

如果您要展開一個可能評估為 null 的表示式,並且希望忽略該 null (且不插入任何元素),請使用空感知展開元素

要了解有關展開運算子的更多資訊,請參閱展開運算子

空感知展開元素

#

空感知展開元素與展開元素類似,但允許集合為 null,如果為 null 則不插入任何內容。

空感知展開元素在集合中具有以下語法:

dart
...?<sequence_expression>

在以下示例中,名為 a 的列表被忽略,因為它為 null,但名為 b 的列表中的元素被新增到名為 items 的列表中。請注意,如果集合本身不為 null,但包含為 null 的元素,則這些 null 元素仍將新增到結果中。

dart
List<int>? a = null;
var b = [1, null, 3];
var items = [0, ...?a, ...?b, 4]; // [0, 1, null, 3, 4]

由於空安全,您不能對可能為 null 的值執行展開操作 (...)。以下示例將產生編譯時錯誤,因為 extraOptions 引數可為空,並且在 extraOptions 上使用的展開運算子不是空感知的。

✗ 靜態分析:失敗dart
List<String> buildCommandLine(
  String executable,
  List<String> options, [
  List<String>? extraOptions,
]) {
  return [
    executable,
    ...options,
    ...extraOptions, // <-- Error
  ];
}

// Usage:
//   buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
//   Compile-time error

如果您想展開一個可為空的集合,請使用空感知展開元素。以下示例有效,因為在 extraOptions 上使用了空感知展開運算子。

dart
List<String> buildCommandLine(
  String executable,
  List<String> options, [
  List<String>? extraOptions,
]) {
  return [
    executable,
    ...options,
    ...?extraOptions, // <-- OK now.
  ];
}

// Usage:
//   buildCommandLine('dart', ['run', 'my_script.dart'], null);
// Result:
//   [dart, run, my_script.dart]

要了解有關空感知展開運算子的更多資訊,請參閱展開運算子

If 元素

#

if 元素根據給定的條件表示式有條件地評估一個內部元素,如果條件為假,則可選地評估另一個 else 元素。

if 元素有幾種語法變體:

dart
// If the bool expression is true, include the result.
if (<bool_expression>) <result>
dart
// If the expression matches the pattern, include the result.
if (<expression> case <pattern>) <result>
dart
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<bool_expression>) <result> else <result>
dart
// If the operation resolves as true, include the first
// result, otherwise, include the second result.
if (<expression> case <pattern>) <result> else <result>

以下示例說明了在集合中使用帶有布林表示式的 if 元素的各種方式:

dart
var includeItem = true;
var items = [0, if (includeItem) 1, 2, 3]; // [0, 1, 2, 3]
dart
var includeItem = true;
var items = [0, if (!includeItem) 1, 2, 3]; // [0, 2, 3]
dart
var name = 'apple';
var items = [0, if (name == 'orange') 1 else 10, 2, 3]; // [0, 10, 2, 3]
dart
var name = 'apple';
var items = [
  0,
  if (name == 'kiwi') 1 else if (name == 'pear') 10,
  2,
  3,
]; // [0, 2, 3]

以下示例說明了在集合中使用帶有 case 部分的 if 元素的各種方式:

dart
Object data = 123;
var typeInfo = [
  if (data case int i) 'Data is an integer: $i',
  if (data case String s) 'Data is a string: $s',
  if (data case bool b) 'Data is a boolean: $b',
  if (data case double d) 'Data is a double: $d',
]; // [Data is an integer: 123, Data is a double: 123]
dart
var word = 'hello';
var items = [
  1,
  if (word case String(length: var wordLength)) wordLength,
  3,
]; // [1, 5, 3]
dart
var orderDetails = ['Apples', 12, ''];
var summary = [
  'Product: ${orderDetails[0]}',
  if (orderDetails case [_, int qty, _]) 'Quantity: $qty',
  if (orderDetails case [_, _, ''])
    'Delivery: Not Started'
  else
    'Delivery: In Progress',
]; // [Product: Apples, Quantity: 12, Delivery: Not Started]

您可以將不同的 if 操作與 else if 部分混合使用。例如:

dart
var a = 'apple';
var b = 'orange';
var c = 'mango';
var items = [
  0,
  if (a == 'apple') 1 else if (a case 'mango') 10,
  if (b case 'pear') 2 else if (b == 'mango') 20,
  if (c case 'apple') 3 else if (c case 'mango') 30,
  4,
]; // [0, 1, 30, 4]

要了解有關 if 條件的更多資訊,請參閱if 語句。要了解有關 if-case 條件的更多資訊,請參閱if-case 語句

For 元素

#

for 元素迭代並重複評估給定的內部元素,插入零個或多個結果值。

for 元素在集合中具有以下語法:

dart
for (<expression> in <collection>) <result>
dart
for (<initialization_clause>; <condition_clause>; <increment_clause>) <result>

以下示例說明了在集合中使用 for 元素的各種方式:

dart
var numbers = [2, 3, 4];
var items = [1, for (var n in numbers) n * n, 7]; // [1, 4, 9, 16, 7]
dart
var items = [1, for (var x = 5; x > 2; x--) x, 7]; // [1, 5, 4, 3, 7]
dart
var items = [1, for (var x = 2; x < 4; x++) x, 7]; // [1, 2, 3, 7]

要了解有關 for 迴圈的更多資訊,請參閱for 迴圈

巢狀控制流元素

#

您可以將控制流元素相互巢狀。這是一種替代其他語言中列表推導式的強大方法。

在以下示例中,只有 numbers 中的偶數包含在 items 中。

dart
var numbers = [1, 2, 3, 4, 5, 6, 7];
var items = [
  0,
  for (var n in numbers)
    if (n.isEven) n,
  8,
]; // [0, 2, 4, 6, 8]

iffor 元素內部立即對集合字面量使用展開操作是常見且符合習慣的做法。例如:

dart
var items = [
  if (condition) oneThing(),
  if (condition) ...[multiple(), things()],
]; // [oneThing, [multiple_a, multiple_b], things]

您可以任意深度地巢狀各種元素。在以下示例中,iffor 和展開元素在集合中相互巢狀:

dart
var nestItems = true;
var ys = [1, 2, 3, 4];
var items = [
  if (nestItems) ...[
    for (var x = 0; x < 3; x++)
      for (var y in ys)
        if (x < y) x + y * 10,
  ],
]; // [10, 20, 30, 40, 21, 31, 41, 32, 42]