TypeScript 中的 Map 和 Set 详解

一、Map(映射)

Map 是 ES6 引入的一种新的数据结构,它类似于对象,也是键值对的集合,但 Map 的键可以是任意类型的值(对象、原始值等),而不仅仅是字符串或 Symbol。

1.基本用法

// 创建一个空Map
const map = new Map();

// 创建时初始化
const initializedMap = new Map([
  ['key1', 'value1'],
  ['key2', 'value2']
]);
// 从对象创建Map
const obj = { a: 1, b: 2 };
const mapFromObject = new Map(Object.entries(obj));

2.常用方法和属性

  1. set(key, value) - 添加或更新键值对

    map.set('name', 'Alice');
    map.set(1, 'number key');
    map.set({}, 'object key');
    map.set(null, 'null key'); // 甚至可以使用null或undefined作为键
    
  2. get(key) - 获取键对应的值

    const name = map.get('name'); // 'Alice'
    const unknown = map.get('non-existent'); // undefined
    
  3. has(key) - 检查是否存在某个键

    const hasName = map.has('name'); // true
    
  4. delete(key) - 删除某个键值对

     const deleted = map.delete('name'); // 返回布尔值表示是否删除成功
    
  5. clear() - 清空所有键值对

    map.clear();
    
  6. size - 获取键值对数量

    const size = map.size;
    

3.遍历方法

  1. keys() - 返回所有键的迭代器

    for (const key of map.keys()) {
     console.log(key);
    }
    // 或者转换为数组
    const keysArray = Array.from(map.keys());
    
  2. values() - 返回所有值的迭代器

    for (const value of map.values()) {
     console.log(value);
    }
    
  3. entries() - 返回所有键值对的迭代器

    for (const [key, value] of map.entries()) {
      console.log(key, value);
    }
    // 可以简写为
    for (const [key, value] of map) {
      console.log(key, value);
    }
    
  4. forEach() - 遍历所有键值对

    map.forEach((value, key) => {
      console.log(key, value);
      // 注意:value在前,key在后,与数组的forEach不同
    });
    

4.TypeScript 中的类型定义

// 明确指定键和值的类型
const typedMap = new Map<string, number>();
typedMap.set('age', 25); // 正确
typedMap.set('name', 'Alice'); // 错误,值应该是number类型

// 复杂类型示例
interface User {
  id: number;
  name: string;
}

const userMap = new Map<number, User>();
userMap.set(1, { id: 1, name: 'Alice' });

// 从现有类型派生Map类型
type UserMap = Map<number, User>;

5.Map 与普通对象的区别

  1. Map 的键可以是任意类型,而对象的键只能是字符串或 Symbol
  2. Map 的大小可以通过 size 属性直接获取,而对象需要手动计算
  3. Map 在频繁增删键值对的场景下性能更好
  4. Map 会维护键值对的插入顺序
  5. Map 可以直接遍历,而对象需要先获取键数组

6.使用场景

  • 需要键不是字符串/数字/Symbol的情况
  • 需要频繁增删键值对的场景
  • 需要维护插入顺序的场景
  • 需要更便捷的遍历和大小获取的场景

二、Set(集合)

Set 是 ES6 引入的另一种数据结构,它类似于数组,但成员的值都是唯一的,没有重复的值。

1.基本用法

// 创建一个空Set
const set = new Set();

// 创建时初始化
const initializedSet = new Set([1, 2, 3, 4]);

// 从类数组对象创建
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
const setFromArrayLike = new Set(Array.from(arrayLike));

// 字符串会被拆分为字符
const charSet = new Set('hello'); // Set {'h', 'e', 'l', 'o'}

2.常用方法和属性

  1. add(value) - 添加值,返回Set本身(可以链式调用)

    set.add(1).add(2).add(3);
    set.add(NaN).add(NaN); // Set中NaN等于自身,只会保留一个
    
  2. has(value) - 检查是否存在某个值

    const hasTwo = set.has(2); // true
    set.has(NaN); // 可以正确检测NaN
    
  3. delete(value) - 删除某个值

    set.delete(2);
    
  4. clear() - 清空所有值

    set.clear();
    
  5. size - 获取值的数量

    const size = set.size;
    

3.遍历方法

  1. values() - 返回所有值的迭代器

    for (const value of set.values()) {
      console.log(value);
    }
    
  2. keys() - 与values()相同,为了与Map兼容

    for (const key of set.keys()) {
      console.log(key);
    }
    
  3. entries() - 返回[value, value]形式的迭代器,为了与Map兼容

    for (const [key, value] of set.entries()) {
      console.log(key, value); // 两者相同
    }
    
  4. forEach() - 遍历所有值

    set.forEach((value) => {
      console.log(value);
      // 注意:参数设计为与Map的forEach一致
    });
    

4.TypeScript 中的类型定义

// 明确指定值的类型
const typedSet = new Set<number>();
typedSet.add(1); // 正确
typedSet.add('1'); // 错误,值应该是number类型

// 复杂类型示例
interface Product {
  id: number;
  name: string;
}

const productSet = new Set<Product>();
productSet.add({ id: 1, name: 'Laptop' });

// 从现有类型派生Set类型
type ProductSet = Set<Product>;

5.Set 与数组的区别

特性Set数组
唯一性值唯一允许重复值
查找效率has方法O(1)includes/indexOf O(n)
添加元素add方法push/unshift等方法
删除元素delete方法splice/filter等方法
顺序插入顺序可排序
索引访问不支持支持下标访问
大小获取size属性length属性
内存占用通常比数组多通常更节省内存

6.使用场景

  • 需要存储唯一值的场景
  • 需要快速检查某个值是否存在的场景
  • 需要去重的场景
  • 数学集合运算(并集、交集、差集)

7.集合运算示例

// 并集
const union = new Set([...setA, ...setB]);

// 交集
const intersection = new Set([...setA].filter(x => setB.has(x)));

// 差集 (A - B)
const difference = new Set([...setA].filter(x => !setB.has(x)));

三、Map 和 Set 的性能特点

  1. 查找速度:Map 和 Set 的查找操作(has/get)平均时间复杂度为 O(1),比数组的 O(n) 快
  2. 插入速度:Map 和 Set 的插入操作平均时间复杂度为 O(1)
  3. 删除速度:Map 和 Set 的删除操作平均时间复杂度为 O(1)
  4. 内存占用:Map 和 Set 通常比对象和数组占用更多内存
  5. 迭代性能:Map和Set的迭代速度与数组相当,对象迭代需要Object.keys等额外步骤

四、应用建议

  1. 类型安全:
// 总是明确指定泛型类型
const userMap = new Map<number, User>(); 
  1. 合理选择数据结构:
  • 需要键值对 → Map

  • 需要唯一值 → Set

  • 需要索引访问 → 数组

  • 需要简单键值且不频繁修改 → 对象

  1. 内存管理:
  • 对于临时对象作为键/值,考虑WeakMap/WeakSet

  • 大型Map/Set不再需要时手动clear()

  1. 性能优化:
  • 避免在热代码路径中频繁创建新的Map/Set

  • 对于大型数据集,考虑预分配大小

  1. 不可变数据:
// 需要不可变Map/Set时
function addToImmutableSet<T>(set: Set<T>, value: T): Set<T> {
  return new Set([...set, value]);
} 
  1. 与数组转换:
// Map转数组
const mapEntries = [...map.entries()];
// Set转数组
const setValues = [...set];
  1. 自定义相等性:
// 对于对象值,需要自定义相等逻辑
const objectSet = new Set<{id: number}>();
const obj1 = {id: 1};
const obj2 = {id: 1};
objectSet.add(obj1);
objectSet.has(obj2); // false,因为对象引用不同

五、总结

特性MapSet
存储内容键值对唯一值
键/值类型任意类型任意类型
主要方法set, get, has, deleteadd, has, delete
遍历方式keys, values, entriesvalues, keys, entries
典型用途需要非字符串键的键值对存储值唯一性检查、集合运算
是否有序插入顺序插入顺序

1.何时选择Map/Set:

  1. 需要非字符串键 → Map

  2. 需要严格维护插入顺序 → Map/Set

  3. 需要频繁增删元素 → Map/Set

  4. 需要高效存在性检查 → Set

  5. 需要集合运算 → Set

2.何时选择对象/数组:

  1. 需要索引访问 → 数组

  2. 简单配置对象 → 对象

  3. 需要方法操作(如map/filter)→ 数组

  4. 小型静态数据集 → 对象/数组

在 TypeScript 中使用 Map 和 Set 时,建议总是明确指定泛型类型参数,以获得更好的类型检查和代码提示。这两种数据结构在处理特定问题时比传统的对象和数组更高效、更直观。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值