You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript如何创建存储唯一元组/复合键对象的集合?

现有方案的问题

  1. 基于数组遍历的实现:数据量增大后has方法是O(n)复杂度,性能会快速下降
  2. 你考虑的数组作为Map键的方案存在隐藏缺陷:JS/TS的Map对对象类型键是引用匹配,两次传入内容完全相同的数组属于不同的引用,Map会判定为两个不同的键,完全达不到去重效果。

可选最优实现方案

方案1:序列化复合键(适合键可序列化的简单场景)

如果你的复合键元素都是数字、字符串、布尔这类可安全序列化的类型,直接把复合键序列化为字符串作为Set的键即可,实现成本最低,查询性能为O(1)。

class Tuple3Set<T1, T2, T3> {
  private store = new Set<string>();
  // 选择不会出现在键值中的分隔符即可
  private readonly SEPARATOR = '|';

  add(item: [T1, T2, T3]): void {
    const key = item.join(this.SEPARATOR);
    if (this.store.has(key)) {
      throw new Error(`key already exists: [${item.join(', ')}]`);
    }
    this.store.add(key);
  }

  has(item: [T1, T2, T3]): boolean {
    return this.store.has(item.join(this.SEPARATOR));
  }
}

// 使用示例
const set = new Tuple3Set<number, number, number>();
set.add([1,2,3]); // 正常添加
set.add([2,1,3]); // 正常添加
set.add([1,2,3]); // 抛出错误:key already exists

优点:实现简单,内存占用低,性能最优
缺点:仅支持可序列化的键类型,需要注意分隔符不能和键值内容冲突


方案2:多层Map结构(通用方案,支持任意类型键)

如果你的键包含对象、Symbol等无法序列化的类型,或者不想处理序列化冲突问题,可以用多层嵌套Map实现,不需要序列化,查询复杂度为O(k)(k为复合键长度),远优于数组遍历的O(n)。

class CompositeKeySet<T extends any[]> {
  private root = new Map();
  // 唯一标记复合键的终点,避免和键值冲突
  private readonly END_FLAG = Symbol('composite_key_end');

  add(...keys: T): void {
    let currentMap = this.root;
    // 逐层创建嵌套Map
    for (const key of keys) {
      if (!currentMap.has(key)) {
        currentMap.set(key, new Map());
      }
      currentMap = currentMap.get(key);
    }
    // 检查键是否已存在
    if (currentMap.has(this.END_FLAG)) {
      throw new Error(`key already exists: [${keys.join(', ')}]`);
    }
    currentMap.set(this.END_FLAG, true);
  }

  has(...keys: T): boolean {
    let currentMap = this.root;
    for (const key of keys) {
      if (!currentMap.has(key)) return false;
      currentMap = currentMap.get(key);
    }
    return currentMap.has(this.END_FLAG);
  }
}

// 使用示例,支持任意长度复合键
const set = new CompositeKeySet<[number, number, number]>();
set.add(1,2,3); // 正常添加
set.add(2,1,3); // 正常添加
set.add(1,2,3); // 抛出错误:key already exists
console.log(set.has(1,2,3)); // true

优点:支持任意类型的键(包括引用类型),无序列化冲突问题,性能稳定,可适配任意长度的复合键
缺点:实现复杂度略高于序列化方案,内存占用略高

如果需要存储每个复合键对应的额外属性,只需要把END_FLAG对应的布尔值替换为你要存储的数据即可,相当于直接实现了复合键的Map结构。

内容的提问来源于stack exchange,提问作者user17298649

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 12:24:04