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); //2
m1['sex'] = 'male'; //新增一个 key-value
m1['age'] = 20; //修改一个 key-value
print(m1); //Map {name: jack, age: 20, sex: male}
print(m1.length); //3
isEmpty、isNotEmpty 是否为空、不为空
var m1 = {'name': 'jack', 'age': 18};
print(m1.isEmpty); //false
print(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)); //1
print(m1); //{a: 1, b: 2, c: 3} Map不变
// 不存在,添加
print(m1.putIfAbsent('d', () => 11)); //11
print(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)); //2
print(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')); //1
print(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')); //true
print(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地址将被多次使用,因此可以从树的根目录找到它们。