Dart Collections
Collections are data structures 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 a group of ordered elements, and duplicates are allowed.
You can access any element in a List through an index (starting from 0).
### Creating a List
## Example
```dart
void main() {
// Create List using literal
List sites = ['TUTORIAL', 'Google', 'GitHub'];
print('Website list: $sites');
// Type inference (omitting generic type)
var numbers = [1, 2, 3, 4, 5];
print('Number list: $numbers');
// Create empty list
var emptyList = [];
// Create fixed-length list using List.filled
var zeros = List.filled(3, 0); // [0, 0, 0]
print('Filled list: $zeros');
// Generate list using List.generate
var squares = List.generate(5, (i) => i * i);
print('Square list: $squares');
}
Website list: [TUTORIAL, Google, GitHub]
Number list: [1, 2, 3, 4, 5]
Filled list: [0, 0, 0]
Square list: [0, 1, 4, 9, 16]
### Accessing and Modifying List Elements
## Example
```dart
void main() {
var fruits = ['Apple', 'Banana', 'Orange'];
// Access through index (starting from 0)
print('First fruit: ${fruits}'); // Apple
print('Last one: ${fruits[fruits.length - 1]}'); // Orange
// Modify element
fruits = 'Grape';
print('After modification: $fruits'); // [Apple, Grape, Orange]
// Get length
print('Fruit count: ${fruits.length}');
// Check if empty
print('List empty? ${fruits.isEmpty}');
print('List not empty? ${fruits.isNotEmpty}');
}
First fruit: Apple
Last one: Orange
After modification: [Apple, Grape, Orange]
Fruit count: 3
List empty? false
List not empty? true
### Common List Operations
## Example
```dart
void main() {
var list = ['TUTORIAL', 'Dart'];
// Add elements
list.add('Flutter'); // Add single
list.addAll(['Google', 'AI']); // Add multiple
print('After adding: $list');
// Insert element (at specified 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 one
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');
}
After adding: [TUTORIAL, Dart, Flutter, Google, AI]
After insertion: [TUTORIAL, Tutorial, Dart, Flutter, Google, AI]
After deletion: [Tutorial, Dart, Flutter]
Contains Dart? true, Position: 1
After sorting: [1, 1, 3, 4, 5, 9]
After reversal: [9, 5, 4, 3, 1, 1]
First 3: [1, 1, 3]
### Functional Methods for List
Dart's List supports functional methods such as map, where, and reduce, making data processing more concise.
## Example
```dart
void main() {
var scores = [55, 78, 92, 60, 45, 88];
// where: filter (keep elements that meet condition)
var passed = scores.where((s) => s >= 60);
print('Passed scores: $passed');
// map: mapping (convert each element to new value)
var grades = scores.map((s) => s >= 60 ? 'Pass' : 'Fail');
print('Grades: $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('TUTORIAL total score: $total');
// fold: cumulative calculation with 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('Has full mark? $hasFullMark');
print('All passed? $allPassed');
}
Passed scores: (78, 92, 60, 88)
Grades: (Fail, Pass, Pass, Pass, Fail, Pass)
High score list: [High score: 92, High score: 88]
TUTORIAL total score: 418
Average score: 69.7
Has full mark? false
All passed? false
> `map()` and `where()` return Iterable (lazy evaluation), which needs to be converted to List using `toList()` to actually execute the calculation. If you only need to iterate once, you can use Iterable directly without conversion.
* * *
## Set: Unique Element Collection
Set is similar to List, but each element in a Set can only appear once, duplicates are not allowed.
Set is unordered, elements cannot be accessed through index.
## Example
```dart
void main() {
// Create Set (elements automatically deduplicated)
Set tags = {'Dart', 'Flutter', 'Dart', 'TUTORIAL'};
print('Tag set: $tags'); // {Dart, Flutter, TUTORIAL}, duplicate Dart is removed
print('Tag count: ${tags.length}'); // 3
// Add element
tags.add('Google');
tags.add('Dart'); // Dart already exists, won't be added again
print('After adding: $tags');
// Delete element
tags.remove('Google');
print('After deletion: $tags');
// Check if contains
print('Contains TUTORIAL? ${tags.contains('TUTORIAL')}');
// 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}
}
Tag set: {Dart, Flutter, TUTORIAL}
Tag count: 3
After adding: {Dart, Flutter, TUTORIAL, Google}
After deletion: {Dart, Flutter, TUTORIAL}
Contains TUTORIAL? true
Intersection: {3, 4}
Union: {1, 2, 3, 4, 5, 6}
Difference: {1, 2}
> A 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 Mapping
Map is used to store key-value pairs (Key-Value Pair), where each key corresponds to one value.
Keys in a Map must be unique, values can be duplicated.
### Creating and Accessing Map
## Example
```dart
void main() {
// Create Map using literal
Map siteInfo = {
'name': 'TUTORIAL',
'url': '',
'type': 'Programming Tutorial',
};
// Access value through key
print('Site name: ${siteInfo['name']}');
print('Site URL: ${siteInfo['url']}');
// Accessing non-existent key returns null
print('Description: ${siteInfo['description']}'); // null
// Add/modify key-value pair
siteInfo['language'] = 'Chinese';
siteInfo['name'] = ''; // Modify value of existing key
print('Updated: $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')}');
}
Site name: TUTORIAL
Site URL:
Description: null
Updated: {name: , url: type: Programming Tutorial, language: Chinese}
All keys: (name, url, type, language)
All values: (, Programming Tutorial, Chinese)
Has url key? true
Has desc key? false
### Common Map Operations
## Example
```dart
void main() {
var scores = {
'tutorial': 95,
'Alice': 87,
'Bob': 72,
};
// Iterate 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; // Doesn't exist, return default value 0
print('Alice: $aliceScore, Eve: $eveScore');
// putIfAbsent: only add when key doesn't exist
scores.putIfAbsent('tutorial', () => 100); // Already exists, not added
scores.putIfAbsent('David', () => 80); // Doesn't exist, add
print('Final: $scores');
// Get length
print('Count: ${scores.length}');
}
tutorial: 95 points
Alice: 87 points
Bob: 72 points
After deleting Bob: {tutorial: 95, Alice: 87}
Alice: 87, Eve: 0
Final: {tutorial: 95, Alice: 87, David: 80}
Count: 3
> Map keys 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
```dart
void main() {
var basics = ['Dart', 'Flutter'];
var advanced = ['Async Programming', 'State Management'];
// ... spread operator: insert elements from another collection
var allCourses = ['TUTORIAL Intro', ...basics, ...advanced];
print('All courses: $allCourses');
// ...? null-safe spread: if collection is null, skip
List? optionalList; // May be null
var safeList = ['First item', ...?optionalList];
print('Safe spread: $safeList'); // Only first item
optionalList = ['Extra content'];
safeList = ['First item', ...?optionalList];
print('Spread with value: $safeList');
}
All courses: [TUTORIAL Intro, Dart, Flutter, Async Programming, State Management]
Safe spread:
Spread with value: [First item, Extra content]
### Collection if and for (Collection Control Flow)
Dart allows direct use of if and for in collection literals, which is very practical syntactic sugar.
## Example
```dart
void main() {
bool showAdmin = true;
// Collection if: decide whether to include element based on condition
var menuItems = [
'Home',
'Tutorial',
if (showAdmin) 'Admin Panel', // Only added when condition is true
'About',
];
print('Menu: $menuItems');
// Collection for: 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 = [
'TUTORIAL Home',
for (var tag in tags) '',
];
print('Links: $links');
}
Menu: [Home, Tutorial, Admin Panel, About]
Doubled: [2, 4, 6]
Links: [TUTORIAL Home,
* * *
## Comparison of Three Collection Types
| Feature | List | Set | Map |
| --- | --- | --- | --- |
| Ordering | Ordered | Unordered | Unordered (but has iteration order) |
| Duplicates Allowed | Allowed | Not allowed | Keys not allowed, values allowed |
| Index Access | Supports [] | Not supported | Access through key [] |
| Typical Scenarios | Ordered list, sequence | Deduplication, tag collection | Key-value mapping, configuration items |
| Create Literal | [] | {} | {key: value} |
> Common beginner mistake: Both Set and Map use {} for literals. The difference is: Map literals have colons (key: value), while Set doesn't. If you write `var x = {}`, Dart will infer it as Map, not Set. To create an empty Set, you must write `var x = {}`.
YouTip