Dart MapBase
最后修改日期:2025 年 6 月 4 日
MapBase 是 Dart 中 Map 的一个抽象基类。它提供了所有 Map 实现共享的通用功能。
此类实现了大多数 Map 操作,但将实际的存储实现留给子类。在创建自定义 Map 类型时非常有用。
基本的 MapBase 实现
这是通过扩展 MapBase 创建简单 Map 的方法。
main.dart
import 'dart:collection';
class SimpleMap<K, V> extends MapBase<K, V> {
final _map = <K, V>{};
@override
V? operator [](Object? key) => _map[key];
@override
void operator []=(K key, V value) => _map[key] = value;
@override
void clear() => _map.clear();
@override
Iterable<K> get keys => _map.keys;
@override
V? remove(Object? key) => _map.remove(key);
}
void main() {
var map = SimpleMap<String, int>();
map['one'] = 1;
map['two'] = 2;
print(map['one']); // 1
print(map.keys); // (one, two)
}
我们实现了 MapBase 中所有必需的抽象方法。实际存储委托给一个私有 Map。这种模式对于自定义 Map 类型很常见。
$ dart main.dart 1 (one, two)
计数 Map 实现
我们可以创建一个专门的 Map 来计算值出现的次数。
main.dart
import 'dart:collection';
class CountingMap<K> extends MapBase<K, int> {
final _counts = <K, int>{};
void increment(K key) {
_counts[key] = (_counts[key] ?? 0) + 1;
}
@override
int? operator [](Object? key) => _counts[key];
@override
void operator []=(K key, int value) => _counts[key] = value;
@override
void clear() => _counts.clear();
@override
Iterable<K> get keys => _counts.keys;
@override
int? remove(Object? key) => _counts.remove(key);
}
void main() {
var counter = CountingMap<String>();
counter.increment('apple');
counter.increment('apple');
counter.increment('banana');
print(counter['apple']); // 2
print(counter['banana']); // 1
}
此 CountingMap 添加了一个自定义的 increment 方法,同时仍然像常规 Map 一样运行。它演示了如何使用特定领域的逻辑扩展 MapBase。
$ dart main.dart 2 1
不区分大小写的 Map
这是一个将键视为不区分大小写的 Map 实现。
main.dart
import 'dart:collection';
class CaseInsensitiveMap<V> extends MapBase<String, V> {
final _map = <String, V>{};
String _normalizeKey(String key) => key.toLowerCase();
@override
V? operator [](Object? key) => _map[_normalizeKey(key as String)];
@override
void operator []=(String key, V value) => _map[_normalizeKey(key)] = value;
@override
void clear() => _map.clear();
@override
Iterable<String> get keys => _map.keys;
@override
V? remove(Object? key) => _map.remove(_normalizeKey(key as String));
}
void main() {
var map = CaseInsensitiveMap<int>();
map['Hello'] = 1;
map['HELLO'] = 2;
map['hello'] = 3;
print(map.length); // 1
print(map['HeLlO']); // 3
}
Map 在存储之前将所有键规范化为小写。相同字符串的不同大小写变体映射到同一个条目。
$ dart main.dart 1 3
默认值 Map
此 Map 在找不到键时返回默认值。
main.dart
import 'dart:collection';
class DefaultValueMap<K, V> extends MapBase<K, V> {
final _map = <K, V>{};
final V defaultValue;
DefaultValueMap(this.defaultValue);
@override
V operator [](Object? key) => _map[key] ?? defaultValue;
@override
void operator []=(K key, V value) => _map[key] = value;
@override
void clear() => _map.clear();
@override
Iterable<K> get keys => _map.keys;
@override
V? remove(Object? key) => _map.remove(key);
}
void main() {
var map = DefaultValueMap<String, int>(0);
map['a'] = 1;
print(map['a']); // 1
print(map['b']); // 0
}
Map 会用一个默认值进行初始化。当访问不存在的键时,它会返回这个默认值而不是 null。这对于计数模式很有用。
$ dart main.dart 1 0
可观察 Map
我们可以创建一个 Map 来通知侦听器更改。
main.dart
import 'dart:collection';
class ObservableMap<K, V> extends MapBase<K, V> {
final Map<K, V> _map = {};
final List<void Function(K, V?)> _listeners = [];
void addListener(void Function(K, V?) listener) {
_listeners.add(listener);
}
@override
V? operator [](Object? key) => _map[key as K];
@override
void operator []=(K key, V value) {
_map[key] = value;
_notifyListeners(key, value);
}
@override
void clear() {
for (var key in _map.keys.toList()) {
_notifyListeners(key, null); // Notify before clearing
}
_map.clear();
}
@override
Iterable<K> get keys => _map.keys;
@override
V? remove(Object? key) {
if (!_map.containsKey(key)) return null;
var value = _map.remove(key);
_notifyListeners(key as K, null);
return value;
}
void _notifyListeners(K key, V? value) {
for (var listener in _listeners) {
listener(key, value);
}
}
}
void main() {
var map = ObservableMap<String, int>();
map.addListener((key, value) {
print('Change: $key = $value');
});
map['a'] = 1;
map['b'] = 2;
map.remove('a');
map.clear(); // Will correctly notify all removals
}
此 Map 维护一个侦听器列表,并通知它们更改。通知包括更改的键及其新值(如果已删除,则为 null)。
最佳实践
- 最小实现:扩展 MapBase 时,仅实现必需的方法。
- 一致的相等性:确保键的相等性与 hashCode 一致。
- 空安全:在您的实现中适当处理 null 键/值。
- 性能:考虑您的存储后端的性能特征。
来源
本教程涵盖了 Dart 的 MapBase,并通过实际示例演示了如何通过扩展此抽象基类来创建自定义 Map 实现。
作者
列出 所有 Dart 教程。