ZetCode

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)。

最佳实践

来源

Dart MapBase 文档

本教程涵盖了 Dart 的 MapBase,并通过实际示例演示了如何通过扩展此抽象基类来创建自定义 Map 实现。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程