Dart: Dart Collections
Last updated: 2026-08-26
Collections are the army of data — ordered lists, deduplicated sets, indexed maps. Combine them and you become invincible.
1. What You'll Learn
- List: Create / Traverse / Sort / Spread (
...) and Collection-if/for Elements - Set: Deduplication and Set Operations (Intersection / Union / Difference)
- Map: Key-Value Operations and Iteration
- Collection Type Inference and Const Collections
- Bob's Scenario: Grouping and Aggregating Millions of Orders
2. A Developer's Real Story
(1) The Pain Point: Manual Grouping and Aggregation is Time-Consuming and Error-Prone
Bob needs to group 1,200,000 orders by category, calculating total revenue and order count for each. He manually implemented the grouping logic with a for loop, writing 50 lines of code, but encountered issues like duplicate key overwriting and unstable sorting. Processing 1,200,000 records took 8 seconds.
(2) The Solution: Collection Operations
Dart's collection operations combined with higher-order functions reduce the grouping and aggregation to just 10 lines of code, with a 40% speed improvement.
// Group orders by category and aggregate
final byCategory = <String, List<Order>>{};
for (final order in orders) {
byCategory.putIfAbsent(order.category, () => []).add(order);
}
// Or use fold for aggregation
final revenueByCategory = orders.fold<Map<String, double>>(
{}, (acc, o) => acc..update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount));
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
(3) The Benefits
- 80% reduction in code volume, with clearer logic
- Set deduplication avoids duplicate data, Map grouping replaces manual indexing
- Spread and collection-if make collection building more declarative
3. List
(1) Creation and Basic Operations
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: List Creation
void main() {
// Literal creation
List<int> counts = [1, 2, 3, 4, 5];
var amounts = <double>[1500.0, 3200.0, 890.0];
// Growable list
var orders = <String>['ORD-001', 'ORD-002'];
orders.add('ORD-003');
// Fixed-length list
var buffer = List<double>.filled(5, 0.0);
buffer[0] = 100.0;
// Generate list
var batch = List.generate(10, (i) => 'Batch-${i + 1}');
print(counts); // [1, 2, 3, 4, 5]
print(orders); // [ORD-001, ORD-002, ORD-003]
print(buffer); // [100.0, 0.0, 0.0, 0.0, 0.0]
print(batch); // [Batch-1, ..., Batch-10]
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: List Traversal and Sorting
void main() {
var amounts = [1500.0, 890.0, 3200.0, 50.0];
// Sort in-place
amounts.sort();
print(amounts); // [50.0, 890.0, 1500.0, 3200.0]
// Sort descending (create new list)
var descending = [...amounts]..sort((a, b) => b.compareTo(a));
print(descending); // [3200.0, 1500.0, 890.0, 50.0]
// Access elements
print(amounts.first); // 50.0
print(amounts.last); // 3200.0
print(amounts[2]); // 1500.0
// Sublist
print(amounts.sublist(1, 3)); // [890.0, 1500.0]
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Spread and Collection-if/for
void main() {
var base = ['ORD-001', 'ORD-002'];
var extra = ['ORD-003', 'ORD-004'];
// Spread operator
var combined = [...base, ...extra];
print(combined); // [ORD-001, ORD-002, ORD-003, ORD-004]
// Null-aware spread
List<String>? maybeList;
var safe = ['header', ...?maybeList, 'footer'];
print(safe); // [header, footer]
// Collection-if
bool includePremium = true;
var tiers = [
'Standard',
if (includePremium) 'Premium',
'Enterprise',
];
print(tiers); // [Standard, Premium, Enterprise]
// Collection-for
var batch = [
for (int i = 1; i <= 3; i++) 'Batch-$i',
];
print(batch); // [Batch-1, Batch-2, Batch-3]
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
| Operation | Syntax | Description |
|---|---|---|
| spread | ...list |
Spreads elements |
| null-aware spread | ...?list |
Spreads a nullable list |
| collection-if | if (cond) expr |
Conditional inclusion |
| collection-for | for (var x in list) expr |
Loop generation |
4. Set
(1) Creation and Set Operations
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Set Deduplication and Operations
void main() {
// Create from list (removes duplicates)
var allOrders = ['ORD-001', 'ORD-002', 'ORD-001', 'ORD-003'];
var uniqueOrders = <String>{...allOrders};
print(uniqueOrders); // {ORD-001, ORD-002, ORD-003}
// Set operations
var setA = {1, 2, 3, 4, 5};
var setB = {4, 5, 6, 7, 8};
print(setA.intersection(setB)); // {4, 5} - Intersection
print(setA.union(setB)); // {1, 2, 3, 4, 5, 6, 7, 8} - Union
print(setA.difference(setB)); // {1, 2, 3} - Difference
// Membership test
print(setA.contains(3)); // true
print(setA.containsAll({1, 2})); // true
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Application of Set in Deduplication Scenarios
void main() {
// Deduplicate categories from order list
var orders = [
{'id': 'ORD-001', 'category': 'Electronics'},
{'id': 'ORD-002', 'category': 'Books'},
{'id': 'ORD-003', 'category': 'Electronics'},
{'id': 'ORD-004', 'category': 'Clothing'},
{'id': 'ORD-005', 'category': 'Books'},
];
var categories = orders.map((o) => o['category'] as String).toSet();
print(categories); // {Electronics, Books, Clothing}
print('Unique categories: ${categories.length}'); // 3
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
| Operation | Method | Result |
|---|---|---|
| Intersection | a.intersection(b) |
Common elements |
| Union | a.union(b) |
All elements |
| Difference | a.difference(b) |
Elements in a not in b |
| Contains | a.contains(e) |
bool |
| Contains All | a.containsAll(b) |
bool |
5. Map
(1) Creation and Operations
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Map Creation and Basic Operations
void main() {
// Literal creation
var config = <String, dynamic>{
'appName': 'DataPipeline',
'maxRecords': 1000000,
'taxRate': 0.08,
'verbose': true,
};
// Access values
print(config['appName']); // DataPipeline
print(config['missing']); // null
print(config['missing'] ?? 'N/A'); // N/A
// Add/update entries
config['outputPath'] = '/tmp/reports'; // Add
config['taxRate'] = 0.10; // Update
// Safe update
config.update('maxRecords', (v) => v * 2, ifAbsent: () => 500000);
// Iterate
config.forEach((key, value) => print(' $key: $value'));
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Map Iteration and Aggregation
void main() {
var orders = [
{'id': 'ORD-001', 'category': 'Electronics', 'amount': 1500.0},
{'id': 'ORD-002', 'category': 'Books', 'amount': 50.0},
{'id': 'ORD-003', 'category': 'Electronics', 'amount': 3200.0},
{'id': 'ORD-004', 'category': 'Clothing', 'amount': 890.0},
{'id': 'ORD-005', 'category': 'Books', 'amount': 120.0},
];
// Group by category
final byCategory = <String, List<Map<String, dynamic>>>{};
for (final order in orders) {
final cat = order['category'] as String;
byCategory.putIfAbsent(cat, () => []).add(order);
}
// Aggregate revenue by category
final revenue = <String, double>{};
for (final entry in byCategory.entries) {
final total = entry.value.fold<double>(
0, (sum, o) => sum + (o['amount'] as double));
revenue[entry.key] = total;
}
// Print results
revenue.forEach((cat, total) =>
print('$cat: \$${total.toStringAsFixed(2)} USD'));
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
6. Collection Type Inference and Const Collections
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Type Inference and Const Collections
void main() {
// Type inference
var list1 = [1, 2, 3]; // List<int>
var list2 = [1, 2, 3.0]; // List<num> (mixed int and double)
var list3 = <String>[]; // Explicit generic for empty list
// Const collections - deeply immutable
const categories = ['Electronics', 'Books', 'Clothing'];
const config = <String, int>{'batchSize': 10000, 'timeout': 30};
const flags = {true, false};
// categories.add('Sports'); // Error! Cannot modify const
// Const in class
const defaultFormats = ['json', 'csv', 'html'];
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
| Feature | var | final | const |
|---|---|---|---|
| Mutable Content | Yes | Yes (immutable reference) | No |
| Type Inference | Automatic | Automatic | Automatic |
| Compile-time Constant | No | No | Yes |
7. Bob's Scenario: Grouping and Aggregating Millions of Orders
flowchart LR A[List: 1.2M Orders] --> B[where: Filter] B --> C[groupBy: Category] C --> D[Map: Category → Orders] D --> E[fold: Aggregate] E --> F[Output: Statistics]
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
▶ Example
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
: Practical Grouping and Aggregation
class Order {
final String id;
final String category;
final double amount;
final String status;
Order({required this.id, required this.category, required this.amount, required this.status});
}
Map<String, Map<String, dynamic>> analyzeOrders(List<Order> orders) {
// Filter completed orders
final completed = orders.where((o) => o.status == 'completed').toList();
// Group by category
final grouped = <String, List<Order>>{};
for (final order in completed) {
grouped.putIfAbsent(order.category, () => []).add(order);
}
// Aggregate per category
final result = <String, Map<String, dynamic>>{};
for (final entry in grouped.entries) {
final total = entry.value.fold<double>(0, (s, o) => s + o.amount);
final avg = total / entry.value.length;
result[entry.key] = {
'count': entry.value.length,
'total': total,
'average': avg,
};
}
return result;
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed'),
];
final analysis = analyzeOrders(orders);
print('=== Category Analysis ===');
for (final entry in analysis.entries) {
final stats = entry.value;
print('${entry.key}:');
print(' Orders: ${stats['count']}');
print(' Total: \$${(stats['total'] as double).toStringAsFixed(2)} USD');
print(' Average: \$${(stats['average'] as double).toStringAsFixed(2)} USD');
}
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
8. Full Example: DataPipeline Collection Analyzer
// ============================================
// DataPipeline Collection Analyzer
// Full demonstration of List, Set, Map operations
// ============================================
class Order {
final String id;
final String category;
final double amount;
final String status;
final String region;
const Order({
required this.id,
required this.category,
required this.amount,
required this.status,
this.region = 'US',
});
}
class OrderAnalyzer {
final List<Order> orders;
OrderAnalyzer(this.orders);
// Unique categories (Set)
Set<String> get categories => orders.map((o) => o.category).toSet();
// Unique regions (Set)
Set<String> get regions => orders.map((o) => o.region).toSet();
// Completed orders (List filter)
List<Order> get completed =>
orders.where((o) => o.status == 'completed').toList();
// Total revenue (fold)
double get totalRevenue =>
completed.fold(0.0, (sum, o) => sum + o.amount);
// Revenue by category (Map grouping)
Map<String, double> get revenueByCategory {
return completed.fold<Map<String, double>>({}, (acc, o) {
acc.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
return acc;
});
}
// Order count by region and status
Map<String, Map<String, int>> get countByRegionStatus {
final result = <String, Map<String, int>>{};
for (final order in orders) {
result.putIfAbsent(order.region, () => {});
result[order.region]!.update(
order.status, (v) => v + 1, ifAbsent: () => 1);
}
return result;
}
// Top N categories by revenue
List<MapEntry<String, double>> topCategories(int n) {
final sorted = revenueByCategory.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
return sorted.take(n).toList();
}
void printReport() {
print('=== DataPipeline Analytics Report ===');
print('Total orders: ${orders.length}');
print('Completed: ${completed.length}');
print('Unique categories: ${categories.length}');
print('Regions: ${regions}');
print('Total revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
print('\n--- Revenue by Category ---');
for (final entry in revenueByCategory.entries) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Top Categories ---');
for (final entry in topCategories(3)) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Orders by Region & Status ---');
for (final region in countByRegionStatus.entries) {
print(' ${region.key}: ${region.value}');
}
}
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed', region: 'US'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending', region: 'US'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed', region: 'US'),
Order(id: 'ORD-006', category: 'Electronics', amount: 2100.0, status: 'completed', region: 'AP'),
];
OrderAnalyzer(orders).printReport();
}
> **Output:** Run locally in DartPad or via `dart run`. All examples in the Dart course are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Output:
=== DataPipeline Analytics Report ===
Total orders: 6
Completed: 5
Unique categories: 3
Regions: {US, EU, AP}
Total revenue: $4660.00 USD
--- Revenue by Category ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Top Categories ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Orders by Region & Status ---
US: {completed: 2, pending: 1}
EU: {completed: 2}
AP: {completed: 1}
❓ FAQ
Q: What's the performance difference between List and Set? A: List stores elements in insertion order, with O(1) index access and O(n) lookup. Set is hash-based, offering O(1) lookup/contains checks but no index access. Use Set for deduplication scenarios and List when order matters.
Q: Can the keys of a Map be any type? A: Yes, but when using custom classes as keys, you must override
==andhashCode; otherwise, the Map cannot find entries correctly. It's recommended to use String or int as keys.
Q: Should I use
[]or<Type>[]for an empty List? A: Use[]when type inference context is available. Use<Type>[]when declaring independently to avoid inferring asList<dynamic>.
Q: Can collection-if and collection-for only be used in List literals? A: No, List, Set, and Map literals all support collection-if and collection-for. For example:
{for (var i in items) i.name: i.value}.
Q: What's the difference between Map's
putIfAbsentandupdate? A:putIfAbsentonly sets the value if the key doesn't exist.updateonly updates the value if the key exists (use theifAbsentparameter to handle non-existent cases).
Q: What's the difference between const collections and final collections? A: A const collection is a compile-time constant, with entirely immutable contents (deep immutability). A final collection has an immutable reference, but the contents can be mutable (e.g., a final List can have elements added).
Q: How can I sort a large List efficiently? A:
List.sort()sorts in-place, which is more efficient than[...list].sort()(which creates a copy). If you don't want to modify the original list, create a copy first with[...list]and then sort.
📖 Summary
- List is ordered and allows duplicates, supporting spread (
...) and collection-if/for for declarative construction. - Set is unordered and prevents duplicates, with O(1) lookup, supporting intersection/union/difference operations.
- Map stores key-value pairs;
putIfAbsentandupdateare powerful tools for grouping and aggregation. - const collections are deeply immutable; final collections have immutable references but mutable contents.
- Higher-order functions combined with collection operations can reduce data processing from 50 lines to just 10.
📝 Exercises
- Basic (Difficulty ⭐): Create a List containing 10 order amounts. Use
whereto filter amounts greater than 500,mapto format them as USD, andreduceto calculate the total sum. - Intermediate (Difficulty ⭐⭐): Given a set of order data, use Map's
putIfAbsentandfoldto group orders by category, calculating the order count and total revenue for each category, and output a formatted report. - Challenge (Difficulty ⭐⭐⭐): Implement a
CollectionPipelineclass that supports chaining operations likewhere,map,sortBy,groupBy, andaggregate, ultimately outputting the analysis results. Hint: Each step returns a new collection or Map.