Map 是无序的键值对集合。
Map-api:https://api.dart.dev/stable/2.10.3/dart-core/Map-class.html
定义常数的Map
var m1 = const {'a': 1};m1['b'] = 2; //报错。不能在添加了
声明方式
// 字面量var m1 = {'age': 18,'age': 20,'your age': 18,true: '正确',Symbol('book'): '时间简史',};print(m1); //Map {"age": 20, "your age": 18, true: "正确", Symbol("book"): "时间简史"}// 构造函数var m2 = new Map();m2['a'] = 1;print(m2); //Map {'a': 1}
注意事项
- key、value可以是任意的数据类型。
- 相同的key只能出现一次。
- 不同的key可以用相同的value值。
- key可以是单引号或者双引号,但是不能不加引号。
类型推断
Map 在声明的时候会进行默认推断,如果尝试在 map 中添加错误类型,那么分析器或者运行时会引发错误。
有关更多信息,请阅读类型推断。
不指定泛型
有默认推断,只能添加特定的键值对组合
// 默认推断初始泛型为 <String, String>var m1 = {'first': 'partridge','second': 'turtledoves','fifth': 'golden rings'};m1['book'] = '时间简史';// m1[1] = 2; //报错,如果键值对不是<String, String>类型,就会报错// m1['age'] = 18; //报错// 默认推断初始泛型为 <String, int>Map m2 = {'year': 2012};m2['age'] = 18;// m2['name'] = 'lili'; //报错,如果键值对不是<String, int>类型,就会报错// m2[1] = 2; //报错
无默认推断,可以添加任意键值对组合
var m3 = {'a': 1, 1: 'aa'};m3[Symbol('sss')] = true;var m4 = {};m4['age'] = 18;m4[Symbol('sss')] = true;
指定泛型
//指定数据类型为 <String, int> 结构Map<String, int> m1 = new Map();m1['age'] = 18;// m1['name'] = 'jack'; //报错
属性
length
var m1 = {'name': 'jack', 'age': 18};print(m1.length); //2m1['sex'] = 'male'; //新增一个 key-valuem1['age'] = 20; //修改一个 key-valueprint(m1); //Map {name: jack, age: 20, sex: male}print(m1.length); //3
isEmpty、isNotEmpty 是否为空、不为空
var m1 = {'name': 'jack', 'age': 18};print(m1.isEmpty); //falseprint(m1.isNotEmpty); //true
keys、values、entries 获取key、value的Iterable形式的集合
var m1 = {'name': 'jack', 'age': 18};print(m1.keys); //Iterable (name, age)print(m1.values); //Iterable (jack, 18)print(m1.entries); //Iterable (MapEntry(name: jack), MapEntry(age: 18))
方法
遍历
for循环不可以用
for…in
var s1 = {'a': 1, 'b': 2};for (var item in s1.keys) {print(item); //依次输出 a b}
forEach
forEach((key, value){}) 用于调用Map的每个键值对,并将 键值对 传递给回调函数。
遍历时,可以修改value值,但是不可以新增或删除key。
var m1 = {'a': 1, 'b': 2, 'c': 3};m1.forEach((key, value) {m1['c'] = 4;});print(m1); //{a: 1, b: 2, c: 4}
map
遍历每个元素 根据参数函数,对keyvalue做出修改,可转换成其他泛型的Map。原Map不变
var m1 = {'a': 1, 'b': 2, 'c': 3};var m2 = m1.map((key, value) {return new MapEntry(key, value * 2);});print(m1); //{a: 1, b: 2, c: 4}print(m2); //{a: 2, b: 4, c: 6}
其它
addAll、addEntries 合并
addAll() 合并另一个Map,泛型要一致。addEntries() 合并另一个Map的entries,
// m1.addAll(m2) key相同时value值后者覆盖前者,前者不存在时则添加进来var m1 = {'a': 1, 'b': 2, 'c': 3};var m2 = {'b': 12, 'd': 4};m1.addAll(m2);print(m1); //{a: 1, b: 12, c: 3, d: 4}// m3.addEntries(m4.entries) key相同时value值后者覆盖前者,前者不存在时则添加进来var m3 = {'a': 1, 'b': 2, 'c': 3};var m4 = {'b': 12, 'd': 4};m3.addEntries(m4.entries);print(m3); //{a: 1, b: 12, c: 3, d: 4}
putlfAbsent 存在获取,不存在添加
存在key就返回该key的value值;
不存在key值,就设置值,并返回新加key的value值。
// 存在,获取print(m1.putIfAbsent('a', () => 11)); //1print(m1); //{a: 1, b: 2, c: 3} Map不变// 不存在,添加print(m1.putIfAbsent('d', () => 11)); //11print(m1); //{a: 1, b: 2, c: 3, d: 11} Map发生改变
update、updateAll 修改
update(key, (value) => ()) 对指定的key-value进行修改,返回值为参数函数的返回值。updateAll((key, value) => ()) 批量修改key-value,无返回值。
// update(key, (value) => ())var m1 = {'a': 1, 'b': 2, 'c': 3};print(m1.update('a', (value) => value * 2)); //2print(m1); //{a: 2, b: 2, c: 3}// m1.update('d', (value) => value * 2); //key 不存在,报错// key不存在 但有ifAbsent参数 返回ifAbsent函数的值 并添加到map中m1.update('d', (value) => value * 2, ifAbsent: () => 10);print(m1); //{a: 2, b: 2, c: 3, d: 10}// updateAll((key, value) => ())var m2 = {'a': 1, 'b': 2, 'c': 3};m2.updateAll((key, value) => value * 2);print(m2); //{a: 2, b: 4, c: 6}
注意:
var m1 = {'a': 1, 'b': 'Jack', 'c': 3};m1.update('a', (value) => value * 2); //报错
remove、removeWhere、clear 删除
remove(key) 删除一个key,返回被删除key的value值。removeWhere((key, vaule) => (bool)) 根据条件批量删除,无返回。clear() 清空Map,无返回。
// remove(key)var m1 = {'a': 1, 'b': 2, 'c': 3};print(m1.remove('a')); //1print(m1); //{b: 2, c: 3}// removeWhere((key, vaule) => (bool))var m2 = {'a': 1, 'b': 2, 'c': 3};m2.removeWhere((key, value) => value > 1);print(m2); //{a: 1}// clear()var m3 = {'a': 1, 'b': 2, 'c': 3};m3.clear();print(m3); //{}
containsKey、containsValue 是否包含
containsKey(key) 是否包含Key。containsValue(value) 是否包含value。
var m1 = {'a': 1, 'b': 2, 'c': 3};print(m1.containsKey('a')); //trueprint(m1.containsValue(1)); //true
cast 泛型类型提升为其父祖类
Map<String, int> m1 = {'a': 1, 'b': 2, 'c': 3};Map<Object, Object> m2 = m1.cast();print(m1); //{a: 1, b: 2, c: 3}m2['d'] = 4;print(m2); //{a: 1, b: 2, c: 3, d: 4}
LinkedHashMap
LinkedHashMap是有序的,它会按照插入顺序进行迭代键:
var ordered = new LinkedHashMap();ordered['32352'] = 'Alice';ordered['95594'] = 'Bob';for (var key in ordered.keys) {print(key);}// 一定是先打印 32352, 然后打印95594
如果改变一个key的值并不会改变key的插入顺序,但如果是先删除再添加就会改变插入顺序:
var ordered = new LinkedHashMap();ordered['32352'] = 'Alice';ordered['95594'] = 'Bob';ordered['45684'] = 'Kal';for (var key in ordered.keys) {print("仅遍历:$key");}ordered['95594'] = 'James';for (var key in ordered.keys) {print("改变一个值:$key");}ordered.remove('95594');ordered['95594'] = 'Kobe';for (var key in ordered.keys) {print("改变一个值:$key");}
输出如下:
仅遍历:32352仅遍历:95594仅遍历:45684改变一个值:32352改变一个值:95594改变一个值:45684删除后再添加:32352删除后再添加:45684删除后再添加:95594
HashMap
HashMap并不会保证维护数据的插入顺序。当去遍历HashMap时,键值对的顺序是无法得到保证的。 可以通过如下方式创建HashMap:
import 'dart:collection';main() {var gifts= new HashMap();}
当你并不关心键值对的顺序的时候可以使用HashMap。 HashMap的源码在此。
SplayTreeMap
伸展树(分裂树)是一种自平衡二叉搜索树,它具有可以可以快速访问最近被访问的元素。它能在O(log n)内完成插入、查找和删除操作。
import 'dart:collection';main() {var gifts= new SplayTreeMap();}
SplayTreeMap要求所有的键都是同一类型的,:
var splayTreeMap = SplayTreeMap();splayTreeMap["1"] = "s";splayTreeMap[1] = "2";
上面的代码语法是没问题的,但运行时是不允许的:
type 'int' is not a subtype of type 'String' of 'other'
对于经常存储和访问的数据(如缓存),SplayTreeMap是一个不错的选择。 原因是他们使用树旋转将一个元素调到根,以便更频繁地访问。 性能来自树的自我优化。 也就是说,频繁访问的元素移动到更靠近顶部。 但是,如果同时经常访问树,那么使用SplayTreeMap几乎没有意义。
举个例子,调制解调器路由器以非常高的速率接收网络数据包。 调制解调器必须决定哪个数据包进入哪个线路。 这可以使用map实现,其中键是IP,值是目标线路。 对于这种情况,SplayTreeMap是一个不错的选择,因为大多数IP地址将被多次使用,因此可以从树的根目录找到它们。
