Sets and Maps
Storing unique values with Set and key-value pairs with Map, and when to reach for each over a List.
阅读需 2 分钟
Alongside List, Dart's two other core collections solve problems a list handles awkwardly: guaranteeing uniqueness, and looking things up by key instead of by position.
Set: unique, unordered values
A Set stores values with no duplicates and, unlike a List, doesn't guarantee any particular iteration order:
Set<String> tags = {'dart', 'flutter', 'mobile'};
tags.add('dart'); // no effect — already present
tags.add('web');
print(tags); // {dart, flutter, mobile, web}
print(tags.contains('flutter')); // true
print(tags.length); // 4The moment you find yourself calling list.contains(x) before list.add(x) just to avoid duplicates, that's a sign you probably want a Set instead — the uniqueness is enforced for you, and .contains() on a Set is also significantly faster than on a List for large collections, since it doesn't need to scan every element.
Sets also support the mathematical operations you'd expect:
final a = {1, 2, 3};
final b = {2, 3, 4};
print(a.union(b)); // {1, 2, 3, 4}
print(a.intersection(b)); // {2, 3}
print(a.difference(b)); // {1}Map: key-value pairs
A Map associates each key with a value, and looks up by key in constant time rather than scanning:
Map<String, int> ages = {
'Alice': 30,
'Bob': 25,
};
ages['Charlie'] = 35; // add a new entry
ages['Alice'] = 31; // update an existing one
print(ages['Bob']); // 25
print(ages['Zoe']); // null — missing key, not an error
print(ages.containsKey('Alice')); // trueReading a missing key returns null rather than throwing, which is why ages['Zoe'] has type int?, not int — the compiler is telling you upfront that a lookup might come back empty.
Safe lookups with default values
putIfAbsent and the ?? operator both handle the "get, or fall back" pattern cleanly:
final scores = <String, int>{'Dara': 90};
int darasScore = scores['Dara'] ?? 0; // 90
int leaScore = scores['Lea'] ?? 0; // 0 — key doesn't exist
scores.putIfAbsent('Lea', () => 0); // only inserts if 'Lea' isn't already a keyIterating a Map
final prices = {'apple': 1.5, 'bread': 3.2};
prices.forEach((item, price) {
print('$item costs \$$price');
});
for (final key in prices.keys) {
print(key);
}
for (final value in prices.values) {
print(value);
}Choosing between List, Set, and Map
- Reach for List when order matters and duplicates are fine (a queue of tasks, a sequence of steps).
- Reach for Set when you need uniqueness and don't care about order (tags, visited IDs, distinct categories).
- Reach for Map when you need to look something up by a meaningful key rather than by position (a user ID to a user record, a word to its count).
Picking the right one up front usually removes an entire category of bugs — like accidental duplicates in a list that should have been a set — before they can happen.