Map 是无序的键值对集合。
Map-api:https://api.dart.dev/stable/2.10.3/dart-core/Map-class.html

定义常数的Map

  1. var m1 = const {'a': 1};
  2. m1['b'] = 2; //报错。不能在添加了

声明方式

  1. // 字面量
  2. var m1 = {
  3. 'age': 18,
  4. 'age': 20,
  5. 'your age': 18,
  6. true: '正确',
  7. Symbol('book'): '时间简史',
  8. };
  9. print(m1); //Map {"age": 20, "your age": 18, true: "正确", Symbol("book"): "时间简史"}
  10. // 构造函数
  11. var m2 = new Map();
  12. m2['a'] = 1;
  13. print(m2); //Map {'a': 1}

注意事项
  • key、value可以是任意的数据类型。
  • 相同的key只能出现一次。
  • 不同的key可以用相同的value值。
  • key可以是单引号或者双引号,但是不能不加引号。

类型推断

Map 在声明的时候会进行默认推断,如果尝试在 map 中添加错误类型,那么分析器或者运行时会引发错误。
有关更多信息,请阅读类型推断。

不指定泛型

有默认推断,只能添加特定的键值对组合
  1. // 默认推断初始泛型为 <String, String>
  2. var m1 = {
  3. 'first': 'partridge',
  4. 'second': 'turtledoves',
  5. 'fifth': 'golden rings'
  6. };
  7. m1['book'] = '时间简史';
  8. // m1[1] = 2; //报错,如果键值对不是<String, String>类型,就会报错
  9. // m1['age'] = 18; //报错
  10. // 默认推断初始泛型为 <String, int>
  11. Map m2 = {'year': 2012};
  12. m2['age'] = 18;
  13. // m2['name'] = 'lili'; //报错,如果键值对不是<String, int>类型,就会报错
  14. // m2[1] = 2; //报错

无默认推断,可以添加任意键值对组合
  1. var m3 = {'a': 1, 1: 'aa'};
  2. m3[Symbol('sss')] = true;
  3. var m4 = {};
  4. m4['age'] = 18;
  5. m4[Symbol('sss')] = true;

指定泛型

  1. //指定数据类型为 <String, int> 结构
  2. Map<String, int> m1 = new Map();
  3. m1['age'] = 18;
  4. // m1['name'] = 'jack'; //报错

属性

length

  1. var m1 = {'name': 'jack', 'age': 18};
  2. print(m1.length); //2
  3. m1['sex'] = 'male'; //新增一个 key-value
  4. m1['age'] = 20; //修改一个 key-value
  5. print(m1); //Map {name: jack, age: 20, sex: male}
  6. print(m1.length); //3

isEmpty、isNotEmpty 是否为空、不为空

  1. var m1 = {'name': 'jack', 'age': 18};
  2. print(m1.isEmpty); //false
  3. print(m1.isNotEmpty); //true

keys、values、entries 获取key、value的Iterable形式的集合

  1. var m1 = {'name': 'jack', 'age': 18};
  2. print(m1.keys); //Iterable (name, age)
  3. print(m1.values); //Iterable (jack, 18)
  4. print(m1.entries); //Iterable (MapEntry(name: jack), MapEntry(age: 18))

方法

遍历

for循环不可以用

for…in

  1. var s1 = {'a': 1, 'b': 2};
  2. for (var item in s1.keys) {
  3. print(item); //依次输出 a b
  4. }

forEach

forEach((key, value){}) 用于调用Map的每个键值对,并将 键值对 传递给回调函数。
遍历时,可以修改value值,但是不可以新增或删除key。

  1. var m1 = {'a': 1, 'b': 2, 'c': 3};
  2. m1.forEach((key, value) {
  3. m1['c'] = 4;
  4. });
  5. print(m1); //{a: 1, b: 2, c: 4}

map

遍历每个元素 根据参数函数,对keyvalue做出修改,可转换成其他泛型的Map。原Map不变

  1. var m1 = {'a': 1, 'b': 2, 'c': 3};
  2. var m2 = m1.map((key, value) {
  3. return new MapEntry(key, value * 2);
  4. });
  5. print(m1); //{a: 1, b: 2, c: 4}
  6. print(m2); //{a: 2, b: 4, c: 6}

其它

addAll、addEntries 合并

addAll() 合并另一个Map,泛型要一致。
addEntries() 合并另一个Map的entries,

  1. // m1.addAll(m2) key相同时value值后者覆盖前者,前者不存在时则添加进来
  2. var m1 = {'a': 1, 'b': 2, 'c': 3};
  3. var m2 = {'b': 12, 'd': 4};
  4. m1.addAll(m2);
  5. print(m1); //{a: 1, b: 12, c: 3, d: 4}
  6. // m3.addEntries(m4.entries) key相同时value值后者覆盖前者,前者不存在时则添加进来
  7. var m3 = {'a': 1, 'b': 2, 'c': 3};
  8. var m4 = {'b': 12, 'd': 4};
  9. m3.addEntries(m4.entries);
  10. print(m3); //{a: 1, b: 12, c: 3, d: 4}

putlfAbsent 存在获取,不存在添加

存在key就返回该key的value值;
不存在key值,就设置值,并返回新加key的value值。

  1. // 存在,获取
  2. print(m1.putIfAbsent('a', () => 11)); //1
  3. print(m1); //{a: 1, b: 2, c: 3} Map不变
  4. // 不存在,添加
  5. print(m1.putIfAbsent('d', () => 11)); //11
  6. print(m1); //{a: 1, b: 2, c: 3, d: 11} Map发生改变

update、updateAll 修改

update(key, (value) => ()) 对指定的key-value进行修改,返回值为参数函数的返回值。
updateAll((key, value) => ()) 批量修改key-value,无返回值。

  1. // update(key, (value) => ())
  2. var m1 = {'a': 1, 'b': 2, 'c': 3};
  3. print(m1.update('a', (value) => value * 2)); //2
  4. print(m1); //{a: 2, b: 2, c: 3}
  5. // m1.update('d', (value) => value * 2); //key 不存在,报错
  6. // key不存在 但有ifAbsent参数 返回ifAbsent函数的值 并添加到map中
  7. m1.update('d', (value) => value * 2, ifAbsent: () => 10);
  8. print(m1); //{a: 2, b: 2, c: 3, d: 10}
  9. // updateAll((key, value) => ())
  10. var m2 = {'a': 1, 'b': 2, 'c': 3};
  11. m2.updateAll((key, value) => value * 2);
  12. print(m2); //{a: 2, b: 4, c: 6}

注意:

  1. var m1 = {'a': 1, 'b': 'Jack', 'c': 3};
  2. m1.update('a', (value) => value * 2); //报错

remove、removeWhere、clear 删除

remove(key) 删除一个key,返回被删除key的value值。
removeWhere((key, vaule) => (bool)) 根据条件批量删除,无返回。
clear() 清空Map,无返回。

  1. // remove(key)
  2. var m1 = {'a': 1, 'b': 2, 'c': 3};
  3. print(m1.remove('a')); //1
  4. print(m1); //{b: 2, c: 3}
  5. // removeWhere((key, vaule) => (bool))
  6. var m2 = {'a': 1, 'b': 2, 'c': 3};
  7. m2.removeWhere((key, value) => value > 1);
  8. print(m2); //{a: 1}
  9. // clear()
  10. var m3 = {'a': 1, 'b': 2, 'c': 3};
  11. m3.clear();
  12. print(m3); //{}

containsKey、containsValue 是否包含

containsKey(key) 是否包含Key。
containsValue(value) 是否包含value。

  1. var m1 = {'a': 1, 'b': 2, 'c': 3};
  2. print(m1.containsKey('a')); //true
  3. print(m1.containsValue(1)); //true

cast 泛型类型提升为其父祖类

  1. Map<String, int> m1 = {'a': 1, 'b': 2, 'c': 3};
  2. Map<Object, Object> m2 = m1.cast();
  3. print(m1); //{a: 1, b: 2, c: 3}
  4. m2['d'] = 4;
  5. print(m2); //{a: 1, b: 2, c: 3, d: 4}

LinkedHashMap

LinkedHashMap是有序的,它会按照插入顺序进行迭代键:

  1. var ordered = new LinkedHashMap();
  2. ordered['32352'] = 'Alice';
  3. ordered['95594'] = 'Bob';
  4. for (var key in ordered.keys) {
  5. print(key);
  6. }
  7. // 一定是先打印 32352, 然后打印95594

如果改变一个key的值并不会改变key的插入顺序,但如果是先删除再添加就会改变插入顺序:

  1. var ordered = new LinkedHashMap();
  2. ordered['32352'] = 'Alice';
  3. ordered['95594'] = 'Bob';
  4. ordered['45684'] = 'Kal';
  5. for (var key in ordered.keys) {
  6. print("仅遍历:$key");
  7. }
  8. ordered['95594'] = 'James';
  9. for (var key in ordered.keys) {
  10. print("改变一个值:$key");
  11. }
  12. ordered.remove('95594');
  13. ordered['95594'] = 'Kobe';
  14. for (var key in ordered.keys) {
  15. print("改变一个值:$key");
  16. }

输出如下:

  1. 仅遍历:32352
  2. 仅遍历:95594
  3. 仅遍历:45684
  4. 改变一个值:32352
  5. 改变一个值:95594
  6. 改变一个值:45684
  7. 删除后再添加:32352
  8. 删除后再添加:45684
  9. 删除后再添加:95594

HashMap

HashMap并不会保证维护数据的插入顺序。当去遍历HashMap时,键值对的顺序是无法得到保证的。 可以通过如下方式创建HashMap

  1. import 'dart:collection';
  2. main() {
  3. var gifts= new HashMap();
  4. }

当你并不关心键值对的顺序的时候可以使用HashMap HashMap的源码在此

SplayTreeMap

伸展树(分裂树)是一种自平衡二叉搜索树,它具有可以可以快速访问最近被访问的元素。它能在O(log n)内完成插入、查找和删除操作。

  1. import 'dart:collection';
  2. main() {
  3. var gifts= new SplayTreeMap();
  4. }

SplayTreeMap要求所有的键都是同一类型的,:

  1. var splayTreeMap = SplayTreeMap();
  2. splayTreeMap["1"] = "s";
  3. splayTreeMap[1] = "2";

上面的代码语法是没问题的,但运行时是不允许的:

  1. type 'int' is not a subtype of type 'String' of 'other'

对于经常存储和访问的数据(如缓存),SplayTreeMap是一个不错的选择。 原因是他们使用树旋转将一个元素调到根,以便更频繁地访问。 性能来自树的自我优化。 也就是说,频繁访问的元素移动到更靠近顶部。 但是,如果同时经常访问树,那么使用SplayTreeMap几乎没有意义。

举个例子,调制解调器路由器以非常高的速率接收网络数据包。 调制解调器必须决定哪个数据包进入哪个线路。 这可以使用map实现,其中键是IP,值是目标线路。 对于这种情况,SplayTreeMap是一个不错的选择,因为大多数IP地址将被多次使用,因此可以从树的根目录找到它们。