- Sử dụng thành thạo
List,Set,Mapvới generic type. - Phân biệt growable / fixed / unmodifiable list.
- Hiểu
Iterablelà lazy — chỉ compute khi consume. - Dùng higher-order method:
map,where,fold,reduce,expand,take,skip,any,every,firstWhere. - Dùng spread operator
...và...?. - Dùng collection-if / collection-for:
[if (cond) x, for (var i in xs) i]. - Hiểu khi nào tách Iterable → List bằng
.toList(). - Sort, group by, unique.
1. 3 collection cơ bản
// List — sequence ordered, allow duplicate
List<int> numbers = [1, 2, 3, 2]; // length 4
// Set — unique, unordered nominal
Set<int> uniqs = {1, 2, 3, 2}; // {1, 2, 3}
// Map — key-value
Map<String, int> ages = {'Việt': 25, 'Anh': 30};
| List | Set | Map | |
|---|---|---|---|
| Ordered | Có (insertion) | Không (default), có với LinkedHashSet | Có (insertion) với LinkedHashMap default |
| Unique | Không | Có | Key unique |
| Lookup | O(n) contains | O(1) avg contains | O(1) avg by key |
| Literal | [] | {} (có element) | {} (có :) |
| Use case | Sequence, order matter | Dedup, fast contains | Lookup by key |
2. List — literal & constructor
// Literal — type inferred
final a = [1, 2, 3]; // List<int>
final b = <String>[]; // Empty List<String> growable
// Constructor
final c = List.filled(5, 0); // [0,0,0,0,0]
final d = List.generate(5, (i) => i*2); // [0,2,4,6,8]
final e = List.of([1, 2, 3]); // copy
final f = List.from([1, 2, 3]); // copy (lenient type)
Growable vs fixed-length
// Growable (default)
final g = <int>[];
g.add(1); // OK
// Fixed
final f = List.filled(5, 0, growable: false);
f[0] = 99; // OK — assign
// f.add(1); // ❌ UnsupportedError
Method thông dụng
final xs = [1, 2, 3];
xs.add(4); // [1,2,3,4]
xs.insert(0, 0); // [0,1,2,3,4]
xs.remove(3); // remove value 3 → [0,1,2,4]
xs.removeAt(0); // remove index 0 → [1,2,4]
xs.length; // 3
xs.indexOf(2); // 1
xs.contains(99); // false
xs.reversed.toList(); // [4,2,1]
xs.sublist(1, 3); // [2,4]
xs.sort(); // in-place mutate
3. Set
final a = {1, 2, 3};
final b = {2, 3, 4};
a.union(b); // {1, 2, 3, 4}
a.intersection(b); // {2, 3}
a.difference(b); // {1}
a.contains(1); // true — O(1) avg, nhanh hơn List.contains O(n)
// Empty set — phải dùng constructor vì {} mặc định là Map
final empty1 = <int>{}; // Set rỗng
final empty2 = <int>[] .toSet();
// final empty3 = {}; // ❌ Đây là Map rỗng
4. Map
final ages = {'Việt': 25, 'Anh': 30};
print(ages['Việt']); // 25
print(ages['X']); // null (key không tồn tại)
ages['Phương'] = 22; // add or update
ages.remove('Anh');
ages.containsKey('Việt'); // true
ages.containsValue(25); // true
ages.keys; // Iterable<String>
ages.values; // Iterable<int>
ages.entries; // Iterable<MapEntry<String, int>>
for (final entry in ages.entries) {
print('${entry.key}: ${entry.value}');
}
// putIfAbsent — pattern thường gặp
final counts = <String, int>{};
for (final word in ['a', 'b', 'a', 'c', 'a']) {
counts[word] = (counts[word] ?? 0) + 1;
}
print(counts); // {a: 3, b: 1, c: 1}
5. Iterable — lazy by design
Iterable là parent của List, Set (cùng cả Stream, sẽ học chương 7).
Method map, where, take trả về Iterable — không compute đến khi consume.
final result = [1, 2, 3].map((x) {
print('mapping $x');
return x * 2;
});
print('created'); // Không in mapping nào!
print(result.toList()); // Bây giờ mới in:
// mapping 1
// mapping 2
// mapping 3
// [2, 4, 6]
Chain dài như xs.where(p).map(f).take(5) KHÔNG iterate 3 lần. Mỗi element đi qua pipe một lần — lazy đảm bảo.
Chỉ khi .toList() hoặc for-in consume, pipe mới chạy.
So với JS Array: JS .map().filter().take(5) tạo array trung gian mỗi step — eager. Dart lazy hiệu quả hơn.
6. Higher-order methods
map — transform
final nums = [1, 2, 3];
final doubled = nums.map((x) => x * 2).toList(); // [2, 4, 6]
final labels = nums.map((x) => '#$x').toList(); // ['#1', '#2', '#3']
where — filter
final evens = [1, 2, 3, 4, 5].where((x) => x % 2 == 0);
print(evens.toList()); // [2, 4]
fold và reduce
final xs = [1, 2, 3, 4];
// fold — có initial value, type kết quả có thể khác element type
final sum = xs.fold<int>(0, (acc, x) => acc + x);
print(sum); // 10
final labels = xs.fold<String>('', (acc, x) => '$acc-$x');
print(labels); // '-1-2-3-4'
// reduce — không initial, type acc phải == element type
final sum2 = xs.reduce((a, b) => a + b);
print(sum2); // 10
// reduce throw nếu Iterable rỗng — fold an toàn hơn
// [].reduce((a, b) => a + b); // Bad state: No element
Khác
final xs = [1, 2, 3, 4, 5];
xs.expand((x) => [x, x*10]).toList(); // [1,10,2,20,3,30,4,40,5,50] — flatMap
xs.take(3).toList(); // [1,2,3]
xs.skip(2).toList(); // [3,4,5]
xs.any((x) => x > 4); // true
xs.every((x) => x > 0); // true
xs.firstWhere((x) => x > 3); // 4 (throw nếu không có)
xs.firstWhere((x) => x > 99, orElse: () => -1); // -1
| JS | Dart |
|---|---|
arr.map(f) | xs.map(f).toList() hoặc .toSet() |
arr.filter(p) | xs.where(p).toList() |
arr.reduce(...) | xs.fold(initial, ...) hoặc xs.reduce(...) |
arr.flatMap(f) | xs.expand(f).toList() |
arr.find(p) | xs.firstWhere(p, orElse: () => null) |
arr.some(p) | xs.any(p) |
arr.every(p) | xs.every(p) |
arr.sort() (mutate) | xs.sort() (mutate) |
7. Spread operator
final a = [1, 2];
final b = [3, 4];
final merged = [...a, ...b, 5]; // [1,2,3,4,5]
// Spread null-safe
List<int>? maybe;
final safe = [0, ...?maybe, 99]; // [0, 99]
8. Collection-if / collection-for
Cú pháp cực Flutter-friendly — xây List/Set/Map có conditional inline.
final isAdmin = true;
final menu = [
'Home',
'Profile',
if (isAdmin) 'Admin Panel',
'Settings',
];
print(menu);
// [Home, Profile, Admin Panel, Settings]
// Collection-for
final doubled = [for (var x in [1, 2, 3]) x * 2];
print(doubled); // [2, 4, 6]
// Kết hợp
final grid = [
for (var row = 0; row < 3; row++)
for (var col = 0; col < 3; col++)
if (row + col == 2) '($row,$col)',
];
print(grid); // [(0,2), (1,1), (2,0)]
Column(
children: [
const Text('Welcome'),
if (user.isPremium) const PremiumBadge(),
for (final item in items) ListTile(title: Text(item.title)),
],
)
9. Sort & comparator
final xs = [3, 1, 4, 1, 5, 9, 2];
xs.sort(); // In-place: [1, 1, 2, 3, 4, 5, 9]
// Comparator custom
xs.sort((a, b) => b.compareTo(a)); // descending
// Sort object — bằng compareTo
class User { final String name; final int age; User(this.name, this.age); }
final users = [User('A', 30), User('B', 25), User('C', 30)];
// Sort theo age tăng, nếu cùng age thì theo tên A-Z
users.sort((a, b) {
final byAge = a.age.compareTo(b.age);
if (byAge != 0) return byAge;
return a.name.compareTo(b.name);
});
sort mutate, không trả new
Khác Kotlin .sorted() trả list mới — Dart sort mutate in-place, return void.
Muốn không mutate: [...xs]..sort() (copy trước).
10. Convert giữa types
final xs = [1, 2, 2, 3];
final set = xs.toSet(); // {1, 2, 3}
final entries = [MapEntry('a', 1), MapEntry('b', 2)];
final m = Map.fromEntries(entries); // {a: 1, b: 2}
// Map.fromIterable — flexible
final nameAges = ['Việt:25', 'Anh:30'];
final map = Map.fromIterable(
nameAges,
key: (e) => e.split(':')[0],
value: (e) => int.parse(e.split(':')[1]),
);
print(map); // {Việt: 25, Anh: 30}
11. Unmodifiable view
final xs = [1, 2, 3];
final view = List.unmodifiable(xs);
// view.add(4); // UnsupportedError
// Note: thay đổi xs vẫn ảnh hưởng đến view (cùng underlying data)
xs.add(99);
print(view); // [1, 2, 3, 99]
Để hoàn toàn isolate: List.unmodifiable([...xs]) — copy + lock.
12. const collection
const a = [1, 2, 3]; // const list — immutable + canonical
// a.add(4); // ❌ Unsupported
const b = [1, 2, 3];
print(identical(a, b)); // true — canonical
// var với const literal — vẫn canonical
var c = const [1, 2, 3];
print(identical(a, c)); // true
13. Performance note
| Operation | List | Set | Map |
|---|---|---|---|
| contains | O(n) | O(1) avg | O(1) avg by key |
| add | O(1) amortized end | O(1) avg | O(1) avg |
| insert at front | O(n) | — | — |
| removeAt(0) | O(n) | — | — |
| sort | O(n log n) | — | — |
| iterate | O(n) | O(n) | O(n) |
Khi cần (a) uniqueness, hoặc (b) contains nhanh. Vd: blacklist 1000 user ID — List.contains O(n), Set.contains O(1).
Convert one-time: final blocked = ids.toSet();.
14. Bài tập
Top 3 premium HN user
Cho List<User> với field age, city, premium. Viết 1 expression chain
lấy email của top 3 premium user theo age giảm dần, ở city 'HN'.
💡 Gợi ý đáp án
final emails = users
.where((u) => u.premium && u.city == 'HN')
.toList()
..sort((a, b) => b.age.compareTo(a.age));
final result = emails.take(3).map((u) => u.email).toList();
Lưu ý: phải .toList() trước ..sort() vì where trả Iterable lazy
không có method sort. .. là cascade — trả về list sau sort.
Tổng giỏ hàng với fold
Refactor for-loop tính tổng giá trị giỏ hàng (List of Item với field price, qty) thành .fold.
💡 Gợi ý đáp án
// Cách for-loop
int total = 0;
for (final item in cart) {
total += item.price * item.qty;
}
// Cách fold
final total = cart.fold<int>(0, (acc, i) => acc + i.price * i.qty);
Group books by author
Cho List<Book> với field author, title. Group thành
Map<String, List<Book>>.
💡 Gợi ý đáp án
// Manual với fold
final grouped = books.fold<Map<String, List<Book>>>({}, (acc, book) {
acc.putIfAbsent(book.author, () => []).add(book);
return acc;
});
// Hoặc với for-loop trực tiếp
final grouped = <String, List<Book>>{};
for (final book in books) {
grouped.putIfAbsent(book.author, () => []).add(book);
}
Package collection có helper groupBy sẵn. Tự implement giúp hiểu logic.
Build menu theo role
Build menu items cho 3 role user khác nhau (guest / user / admin) dùng collection-if. So sánh với cách viết if/else thông thường — đếm số dòng.
💡 Gợi ý đáp án
List<String> buildMenu(String role) => [
'Home',
if (role != 'guest') 'Profile',
if (role == 'user' || role == 'admin') 'Settings',
if (role == 'admin') 'Admin Panel',
if (role == 'admin') 'User Management',
'Help',
];
print(buildMenu('guest')); // [Home, Help]
print(buildMenu('user')); // [Home, Profile, Settings, Help]
print(buildMenu('admin')); // tất cả
Cách if/else cần var + push: dài 2-3x dòng. Collection-if đẹp hơn.
Lazy prime generator
Tạo function Iterable<int> primes() trả về vô hạn prime — dùng generator (sync*).
Demo primes().take(10).toList() không loop vô hạn.
💡 Gợi ý đáp án
Iterable<int> primes() sync* {
var n = 2;
while (true) {
if (isPrime(n)) yield n;
n++;
}
}
bool isPrime(int n) {
if (n < 2) return false;
for (var i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
void main() {
print(primes().take(10).toList());
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
}
sync* + yield là generator. take(10) stop generator sau 10 giá trị —
không phải loop vô hạn.
15. Quiz
<int>[].runtimeType ≈ gì?
Xem đáp án
Đáp án: List<int> (growable). Cụ thể là JSArray<int> ở web hoặc _GrowableList<int> ở native — internal.
{1, 2, 3} là Set hay Map?
Xem đáp án
Đáp án: Set. Analyzer infer qua nội dung — không có : → set. {1: 'a'} là Map.
{} rỗng là Set hay Map?
Xem đáp án
Đáp án: Map. Không có context infer. Set rỗng phải <int>{} hoặc <String>{}.
[1,2,3].map((x) => x * 2) trả type gì?
Xem đáp án
Đáp án: Iterable<int>, không phải List<int>. Lazy. Cần .toList() để materialize.
xs.sort() mutate xs hay trả mới?
Xem đáp án
Đáp án: Mutate in-place. Return void. Khác Kotlin .sorted() (immutable). Muốn không mutate: [...xs]..sort().
Cách tìm element đầu thỏa điều kiện hoặc null nếu không có?
Xem đáp án
Đáp án: xs.firstWhere(pred, orElse: () => defaultValue). Hoặc dùng firstWhereOrNull từ package collection.
Spread [...xs, ...?maybeYs] — khi maybeYs null, kết quả?
Xem đáp án
Đáp án: Chỉ chứa elements của xs. ...? null-safe skip. ... thường sẽ throw nếu null.
Map.fromEntries([MapEntry('a', 1), MapEntry('b', 2)]) ≈ gì?
Xem đáp án
Đáp án: {'a': 1, 'b': 2}. Pattern thường dùng khi build map từ list pair.
16. Tổng kết
- ✅ 3 collection: List (ordered), Set (unique), Map (key-value).
- ✅ Iterable lazy — compute on consume.
- ✅ Higher-order:
map,where,fold,reduce,expand,take,skip,any,every,firstWhere. - ✅ Spread
...+ null-safe...?. - ✅ Collection-if / collection-for — Flutter-friendly.
- ✅ Sort in-place + comparator custom.
- ✅
constcollection canonical. - ✅ Performance: List contains O(n), Set/Map O(1) avg.
17. Kết nối
- Chương 7 — Stream: Stream là Iterable async.
- Chương 8: Generic List<T>, collection pattern matching.
- Flutter chương 3 — Layout:
children: <Widget>[...]với collection-if. - Flutter chương 9+: state list entity (List<Todo>, etc).