Dart Collection Types

A collection is a data structure used to store multiple data items.

Dart has three built-in core collection types: List, Set, and Map. This chapter will introduce their usage one by one.


List: Ordered List

List is the most commonly used collection type, used to store an ordered group of elements, allowing duplicates.

You can access any element in a List by its index (starting from 0).

Create List

Example

void main() {
  // Create a List using literals
  List<String> sites = ['EXAMPLE', 'Google', 'GitHub'];
  print(Website list: $sites);

  // Type inference (generic types omitted)
  var numbers = [1, 2, 3, 4, 5];
  print(Number list: $numbers);

  // Create an empty list
  var emptyList = <String>[];

  // Create a fixed-length list using List.filled
  var zeros = List.filled(3, 0);  // [0, 0, 0]
  print(Filled list: $zeros);

  // Generate a list using List.generate
  var squares = List.generate(5, (i) => i * i);
  print(Square numbers list: $squares);
}
网站列表: [EXAMPLE, Google, GitHub]
数字列表: [1, 2, 3, 4, 5]
填充列表: [0, 0, 0]
平方数列表: [0, 1, 4, 9, 16]

Accessing and Modifying List Elements

Example

void main() {
  var fruits = [Apple, Banana, Orange];

  // Access by index (starting from 0)
  print('First fruit: ${fruits[0]}');  // Apple
  print('Last: ${fruits[fruits.length - 1]}');  // Orange

  // Modify element
  fruits[1] = 'Grape';
  print('After modification: $fruits');  // [apple, grape, orange]

  // Get length
  print('Fruit count: ${fruits.length}');

  // Check if empty
  print('Is the list empty? ${fruits.isEmpty}');
  print('Is the list non-empty? ${fruits.isNotEmpty}');
}
第一个水果: 苹果
最后一个: 橙子
修改后: [苹果, 葡萄, 橙子]
水果数量: 3
列表为空? false
列表非空? true

Common List Operations

Example

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

  // Add elements
  list.add('Flutter');           // Add single
  list.addAll(['Google', 'AI']);  // Add multiple
  print('After adding: $list');

  // Insert element (at specific position)
  list.insert(1, 'Tutorial');
  print('After insertion: $list');

  // Delete elements
  list.remove('AI');             // Delete by value
  list.removeAt(0);              // Delete by index
  list.removeLast();             // Delete last
  print('After deletion: $list');

  // Find element
  bool hasDart = list.contains('Dart');
  int index = list.indexOf('Dart');
  print('Contains Dart? $hasDart, position: $index');

  // Sort
  var nums = [3, 1, 4, 1, 5, 9];
  nums.sort();
  print('After sorting: $nums');

  // Reverse
  var reversed = nums.reversed.toList();
  print('After reversal: $reversed');

  // Get sublist
  var subList = nums.sublist(0, 3);
  print('First 3: $subList');
}
添加后: [EXAMPLE, Dart, Flutter, Google, AI]
插入后: [EXAMPLE, 教程, Dart, Flutter, Google, AI]
After deletion: [教程, Dart, Flutter]
包含 Dart? true, 位置: 1
排序后: [1, 1, 3, 4, 5, 9]
反转后: [9, 5, 4, 3, 1, 1]
前3个: [1, 1, 3]

Functional Methods on List

Dart's List supports functional methods like map, where, and reduce, making data processing more concise.

Example

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

  // where: filter (retain elements that satisfy the condition)
  var passed = scores.where((s) => s >= 60);
  print('Passing score: $passed');

  // map: mapping (convert each element to a new value)
  var grades = scores.map((s) => s >= 60 ? 'Pass' : 'Fail');
  print('Grade: $grades');

  // where + map chained call
  var highScores = scores
      .where((s) => s >= 80)
      .map((s) => 'High score: $s')
      .toList();
  print('High score list: $highScores');

  // reduce: cumulative calculation
  var total = scores.reduce((sum, s) => sum + s);
  print('EXAMPLE total score: $total');

  // fold: cumulative calculation with an initial value
  var avg = scores.fold(0, (sum, s) => sum + s) / scores.length;
  print('Average score: ${avg.toStringAsFixed(1)}');

  // any / every: existence check
  bool hasFullMark = scores.any((s) => s == 100);
  bool allPassed = scores.every((s) => s >= 60);
  print('Is there a perfect score? $hasFullMark');
  print('All passed? $allPassed');
}
及格分数: (78, 92, 60, 88)
评级: (不及格, 及格, 及格, 及格, 不及格, 及格)
高分列表: [高分: 92, 高分: 88]
EXAMPLE 总分: 418
平均分: 69.7
有满分吗? false
全部及格? false

map() and where() return an Iterable (lazy evaluation). You need to convert it to a List with toList() for the computation to actually execute. If you only need to iterate once, you can use the Iterable directly without conversion.


Set: Collection of Unique Elements

Set is similar to List, but each element in a Set can appear only once; duplicates are not allowed.

Set is unordered, and elements cannot be accessed by index.

Example

void main() {
  // Create a Set (elements are automatically deduplicated)
  Set<String> tags = {'Dart', 'Flutter', 'Dart', 'EXAMPLE'};
  print('Tag set: $tags');  // {Dart, Flutter, EXAMPLE}, the duplicate Dart was removed
  print('Tag count: ${tags.length}');  // 3

  // Add elements
  tags.add('Google');
  tags.add('Dart');  // Dart already exists, it will not be added again
  print('After adding: $tags');

  // Delete elements
  tags.remove('Google');
  print('After deleting: $tags');

  // Check if contains
  print('Contains EXAMPLE? ${tags.contains('EXAMPLE')}');

  // Set operations
  var setA = {1, 2, 3, 4};
  var setB = {3, 4, 5, 6};

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

The typical use case for Set is deduplication. When you don't need duplicate elements, using Set is much more efficient than manually deduplicating with List.


Map: Key-Value Pair Mapping

Map is used to store key-value pairs. Each key corresponds to a value.

Keys in a Map must be unique; values can be duplicated.

Creating and Accessing Map

Example

void main() {
  // Create a Map with literals
  Map<String, String> siteInfo = {
    'name': 'EXAMPLE',
    'url': 'https://www.example.com',
    'type': 'Programming Tutorial',
  };

  // Access value by key
  print('Site name: ${siteInfo['name']}');
  print('Site URL: ${siteInfo['url']}');

  // Accessing a nonexistent key returns null
  print('Description: ${siteInfo['description']}');  // null

  // Add/Modify key-value pairs
  siteInfo['language'] = 'Chinese';
  siteInfo['name'] = 'example.com';  // Modify the value of an existing key
  print('After update: $siteInfo');

  // Get all keys and all values
  print('All keys: ${siteInfo.keys}');
  print('All values: ${siteInfo.values}');

  // Check if key exists
  print('Has url key? ${siteInfo.containsKey('url')}');
  print('Has desc key? ${siteInfo.containsKey('desc')}');
}
站点名称: EXAMPLE
站点 URL: https://www.example.com
描述: null
更新后: {name: example.com, url: https://www.example.com, type: 编程教程, language: 中文}
所有键: (name, url, type, language)
所有值: (example.com, https://www.example.com, 编程教程, 中文)
有 url 键? true
有 desc 键? false

Common Map Operations

Example

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

  // Iterate over Map
  scores.forEach((name, score) {
    print('$name: $score points');
  });

  // Delete key-value pair
  scores.remove('Bob');
  print('After deleting Bob: $scores');

  // Get value or default value
  int aliceScore = scores['Alice'] ?? 0;
  int eveScore = scores['Eve'] ?? 0;  // If absent, return default value 0
  print('Alice: $aliceScore, Eve: $eveScore');

  // putIfAbsent: add only when the key is absent
  scores.putIfAbsent('example', () => 100);  // Already exists, do not add
  scores.putIfAbsent('David', () => 80);    // If absent, add
  print('Final: $scores');

  // Get length
  print('Number of people: ${scores.length}');
}
example: 95 分
Alice: 87 分
Bob: 72 分
删除 Bob 后: {example: 95, Alice: 87}
Alice: 87, Eve: 0
最终: {example: 95, Alice: 87, David: 80}
人数: 3

Keys in a Map can be of any type (String, int, etc.), but key equality must be meaningful. When using custom objects as keys, you need to ensure that == and hashCode are correctly implemented.


Collection spread operators ... and ...?

The spread operator can "spread" the elements of one collection into another collection.

Example

void main() {
  var basics = ['Dart', 'Flutter'];
  var advanced = ['Asynchronous programming', 'State management'];

  // ... spread operator: insert elements from another collection
  var allCourses = ['EXAMPLE Getting Started', ...basics, ...advanced];
  print('All courses: $allCourses');

  // ...? null-aware spread: skip if collection is null
  List<String>? optionalList;  // May be null
  var safeList = ['First item', ...?optionalList];
  print('Safe spread: $safeList');  // Only the first item

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

if and for in collections (collection control flow)

Dart allows using if and for directly in collection literals; this is very useful syntactic sugar.

Example

void main() {
  bool showAdmin = true;

  // Collection if: conditionally include elements
  var menuItems = [
    'Home',
    'Tutorial',
    if (showAdmin) 'Admin panel',  // Add only when condition is true
    'About',
  ];
  print('Menu: $menuItems');

  // for in a collection: generate elements from another collection
  var numbers = [1, 2, 3];
  var doubled = [
    for (var n in numbers) n * 2,  // Iterate to generate new elements
  ];
  print(Doubled: $doubled);

  // if + for can be combined
  var tags = ['Dart', 'Flutter'];
  var links = [
    'EXAMPLE Home',
    for (var tag in tags) 'https://example.com/$tag',
  ];
  print('Links: $links');
}
菜单: [首页, 教程, 管理后台, 关于]
翻倍: [2, 4, 6]
链接: [EXAMPLE 首页, https://example.com/Dart, https://example.com/Flutter]

Comparison of Three Collections

FeaturesListSetMap
orderingOrderedUnorderedUnordered (but has iteration order)
Duplicates allowedAllowedNot allowedKeys must be unique, values may be duplicated
Index accessSupports []Not supportedAccess via key []
Typical scenariosOrdered List, SequenceDeduplication, Tag CollectionKey-value mapping, configuration items
Create literals[]{}{key: value}

Notes

Set and Map both use{}As a literal.

The difference is:

  • Map usagekey: valueForm (with colon)
  • Set directly writes elements (without a colon)

For example:

var map = {'name': 'Tom'};   // Map
var set = {'Tom', 'Jerry'}; // Set

Special attention needed:

var x = {};

This will not create an empty Set; instead, Dart will infer it as:

Map<dynamic, dynamic>

Because empty curly braces cannot determine the element type, Dart treats it as a Map by default.

To create an empty Set, you must explicitly specify:

var x = <String>{};

or

:
Set<String> x = {};
other extensions