dart

Dart 基础 – 内置类型 – Map 集合

内容纲要

版权归作者 ©刘龙宾 所有,本文章未经作者允许,禁止私自转载!

Map 是一个无序的 key-value (键值对)集合,就是大家熟知的 dictionary 或者 hash。 Map 将 kay 与 value 关联,以便于检索。

通常来说,Map 是用来关联 keysvalues 的对象。其中键和值都可以是任何类型的对象。每个只能出现一次,但是可以重复出现多次。

1. 创建 Map 集合

1.1 基于字面量创建 Map

1.1.1 强类型的 Map

声明强类型 Map 的语法格式为 <keyType, valueType>{},示例代码如下:

void main(List<String> args) {
  // 1. 创建空的 Map 集合
  // 键是字符串,值是字符串的数组
  var hobbies1 = <String, List<String>>{};
  // 输出 true,证明 hobbies1 是空 Map
  print(hobbies1.isEmpty);

  // 2. 创建非空的 Map 集合
  // 键是字符串,值是字符串的数组
  var hobbies2 = <String, List<String>>{
    'liulongbin': ['games', 'coding', 'music'],
    'escook': ['eat', 'sleep', 'run']
  };
  // 输出 2,证明 hobbies2 中包含两个键值对
  print(hobbies2.length);
}

1.1.2 弱类型的 Map

直接使用 {} 字面量定义的就是弱类型的 Map,它的完整写法是 <dynamic, dynamic>{}。弱类型 Map 的好处是可以向 Map 集合中添加任意类型的键值对。示例代码如下:

void main(List<String> args) {
  // 1. 声明键和值都为 dynamic 类型的 Map,
  // 完整写法是 var maps = <dynamic, dynamic>{};
  var maps = {};

  // 2. 向 Map 中添加键值对
  // 键是 String,值是 List<String>
  maps['liulongbin'] = ['games', 'coding', 'music'];
  // 键是 int,值是 String
  maps[1] = 'hello';
  // 键是 int,值是 String
  maps[2] = 'world';

  // 3. 输出 {liulongbin: [games, coding, music], 1: hello, 2: world}
  print(maps);
}

1.1.3 弱类型的键或弱类型的值

在使用 Map 时,还可以单独指定弱类型的键,或单独指定弱类型的值,示例代码如下:

void main(List<String> args) {
  // 1. 定义弱类型的键,强类型的值
  var map1 = <dynamic, String>{};
  // 2. 定义强类型的键,弱类型的值
  var map2 = <int, dynamic>{};

  // 1.1 向 map1 中添加三个键值对
  map1[0] = 'red';
  map1[true] = 'blue';
  map1['flag'] = 'green';

  // 1.2 输出 {0: red, true: blue, flag: green}
  print(map1);

  // 2.1 向 map2 中添加四个键值对
  map2[0] = 'red';
  map2[1] = false;
  map2[2] = 999;
  map2[3] = <String>['A', 'B', 'C'];

  // 2.2 输出 {0: red, 1: false, 2: 999, 3: [A, B, C]}
  print(map2);
}

1.2 基于构造函数创建 Map

基于 Map() 构造函数同样可以创建弱类型和强类型的 Map,示例代码如下:

void main(List<String> args) {
  // 1. 创建弱类型的空 Map
  var map1 = Map();
  // 2. 创建强类型的空 Map
  var map2 = Map<int, String>();
}

2. 操作 Map 元素

2.1 添加元素

2.1.1 直接使用 [] 添加元素

向 Map 集合中添加元素最常用的方式是 [],示例代码如下:

void main(List<String> args) {
  // 1. 定义空 Map
  var names = <int, String>{};
  // 2. 添加元素
  names[0] = 'liulongbin';
  // 3. 输出 {0: liulongbin}
  print(names);
}

2.1.2 addAll

还可以调用 addAll 方法,把指定 Map 中的元素添加到当前 Map 中,示例代码如下:

void main(List<String> args) {
  // 1. 定义两个 Map
  var names = <int, String>{0: 'liulongbin', 1: 'escook'};
  var others = <int, String>{1: 'zs', 2: 'ls', 3: 'xyz'};

  // 2. 把 others 中的键值对添加到 names 中
  // names 和 others 重复的键,会被 others 中对应的值覆盖
  names.addAll(others);
  // 3. 输出 {0: liulongbin, 1: zs, 2: ls, 3: xyz}
  print(names);
}

2.2 修改元素

2.2.1 直接使用 [] 修改

修改元素的值,最简单的方式就是利用 [] 为指定的键重新赋值,示例代码如下:

void main(List<String> args) {
  var names = <String, int>{'liulongbin': 18};
  // 输出 {liulongbin: 18}
  print(names);

  // 使用 [] 为指定的 key 重新赋值
  names['liulongbin'] = 20;
  // 输出 {liulongbin: 20}
  print(names);
}

2.2.2 update

调用 Map 对象的 update 方法,可以更新指定 key 的值,示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map 对象
  var names = <String, int>{'liulongbin': 18};

  // 2. 调用 update 方法,
  // 参数1:要更新的 key
  // 参数2:匿名函数,形参 value 是 key 当前对应的值,函数必须返回一个值当作 key 的新值
  // 返回值:更新后的新值
  var result = names.update('liulongbin', (value) => value + 1);

  // 3. 输出 {liulongbin: 19}
  print(names);
  // 4. 输出 19
  print(result);
}

同时 update 函数还接收第三个参数 ifAbsent,它是一个匿名函数,用来提供一个缺省的候补值。如果在更新属性值的时候发现 Map 中不包含当前 key,则为当前 Map 新增 key 属性,属性值就是这个候补值。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map 对象
  var names = <String, int>{'liulongbin': 18};

  // 2. 调用 update 方法,
  // 参数1:要更新的 key
  // 参数2:匿名函数,形参 value 是 key 当前对应的值,函数必须返回一个值当作 key 的新值
  // 参数3:候补值,如果 key 不存在,则为 Map 新增 key 属性,属性值是当前的候补值
  // 返回值:更新后的新值
  var result = names.update('escook', (value) => value + 1, ifAbsent: () => 0);

  // 3. 输出 {liulongbin: 18, escook: 0}
  // 由于 names 对象中原本不包含 escook 属性,
  // 所以 update 方法为 names 新增了 escook 属性,属性值为参数3提供的候补值
  print(names);
  // 4. 输出 0
  print(result);
}

2.2.3 updateAll

updateAll 可以批量更新 Map 中的每个值,示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{'liulongbin': 'A', 'escook': 'D'};

  // 2. 在每个属性值的后面添加 + 后缀
  scores.updateAll((key, value) => '$value+');

  // 3. 输出 {liulongbin: A+, escook: D+}
  print(scores);
}

2.3 删除元素

2.3.1 remove

remove 方法根据 key 从 Map 中移除键值对,并返回移除的 value 值。如果移除的 key 不存在,则返回 null。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{'liulongbin': 'A', 'escook': 'D'};

  // 2. 移除 escook 键以及对应的值
  var result = scores.remove('escook');

  // 3.1 输出 D
  print(result);
  // 3.2 输出 {liulongbin: A}
  print(scores);

  // ----

  // 4. 移除不存在的属性 abc
  var result2 = scores.remove('abc');

  // 5. 输出 null
  print(result2);
}

2.3.2 removeWhere

removeWhere 可以根据给定的条件,移除符合条件的元素,示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{
    'liulongbin': 'A',
    'escook': 'D',
    'zs': 'C',
    'ls': 'D'
  };

  // 2. 移除分数为 D 的元素,或 key 为 'zs' 的元素
  scores.removeWhere((key, value) => value == 'D' || key == 'zs');

  // 3. 输出 {liulongbin: A}
  print(scores);
}

2.4 清空元素

clear 方法可以清空 Map 集合,示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{'liulongbin': 'A', 'escook': 'D'};
  // 2. 清空所有键值对
  scores.clear();
  // 3. 输出 true,证明 scores 已为空
  print(scores.isEmpty);
}

3. Map 常用方法

3.1 containsKey

containsKey 用来判断 Map 中是否包含指定的 key,如果包含则返回 true,如果不包含则返回 false。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{
    'liulongbin': 'A',
    'escook': 'D',
    'zs': 'C',
    'ls': 'D'
  };

  var res1 = scores.containsKey('escook');
  print(res1); // 输出 true
  var res2 = scores.containsKey('nbb');
  print(res2); // 输出 false
}

3.2 containsValue

containsValue 用来判断 Map 中是否包含指定的 value,如果包含则返回 true,如果不包含则返回 false。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{
    'liulongbin': 'A',
    'escook': 'D',
    'zs': 'C',
    'ls': 'D'
  };

  var res1 = scores.containsValue('A');
  print(res1); // 输出 true
  var res2 = scores.containsValue('B');
  print(res2); // 输出 false
}

3.3 putIfAbsent

putIfAbsent 方法会为对象添加原本不存在的属性,并为其赋值。如果要添加的属性已存在,则不会覆盖仅把此属性的原值返回。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{'liulongbin': 'A', 'escook': 'D'};

  // 2. 检测到 scores 中已包含 escook 属性,
  // 因此不会重新设置 escook 属性的值,只是单纯的把原属性值 'D' 返回给变量 result
  var result = scores.putIfAbsent('escook', () => 'C');

  // 3.1 输出 {liulongbin: A, escook: D},没有任何变化
  print(scores);
  // 3.2 输出 D,原值
  print(result);

  // ----

  // 4. 检测到 scores 中不包含 llb 属性,
  // 因此会给 scores 对象添加新属性 llb,属性值为字符串 A+
  var result2 = scores.putIfAbsent('llb', () => 'A+');
  // 5.1 输出 {liulongbin: A, escook: D, llb: A+}
  print(scores);
  // 5.2 输出 A+
  print(result2);
}

注意:如果您想覆盖原属性值添加原本没有的新属性,建议使用 update 方法或使用 [] 的方式。

3.4 forEach

forEach 方法可以遍历当前 Map 对象中的每个键值对,并对他们进行自定义的操作。示例代码如下:

void main(List<String> args) {
  // 1. 定义 Map
  var scores = <String, String>{'liulongbin': 'A', 'escook': 'D'};

  // 2. 遍历 scores 中的每个键值对
  scores.forEach((key, value) {
    print('学生 $key 的成绩为 $value');
  });
}

注意:List 数组、Set 集合、Map 都支持 forEach 方法对象集合中的元素进行遍历操作,而且用法相同。

Map 可以像 List 一样支持使用扩展操作符(......?)以及集合的 iffor 操作。关于 Map 集合的更多操作,请参考 Map 的官方文档

版权归作者 ©刘龙宾 所有,本文章未经作者允许,禁止私自转载!。

一个自由の前端程序员

留言

您的电子邮箱地址不会被公开。 必填项已用 * 标注