如何使用 Map 实现 Set

关于 MapSet 是两个抽象数据结构,Map 存储一个键值对集合,其中键不重复,Set 存储一个不重复的元素集合。本质上 Set 可以视为一种特殊的 MapSet 其实就是 Map 中的键。

这里我们用 TypeScript 使用 Map 来实现 Set。核心的思路非常简单,创建一个 Map,我们只要用 Map 的键就行,键对应的值全部填充 undefined

class NewSet<T extends unknown> {
  private collection: Map<T, undefined>;
  
  constructor(iterable: T[] = []) {
    this.collection = new Map(
      iterable.map(it => [it, undefined])
    );
  }
}

注意在 TS 中使用 参数默认值 的时候,TS 会自动将这个参数推导为可变参数,例如上面这个会推导为 NewSet<T>(iterable?: T[]): NewSet<T>

接来下补充一些添加、删除的方法:

class NewSet<T extends unknown> {
  private collection: Map<T, undefined>;

  constructor(iterable: T[] = []) {
    this.collection = new Map(
      iterable.map(it => [it, undefined])
    );
  }

  public add(value: T): NewSet<T> {
    this.collection.set(value, undefined);
    return this;
  }

  public delete(value: T): boolean {
    return this.collection.delete(value);
  }

  public has(value: T): boolean {
    return this.collection.has(value);
  }

  public size(): number {
    return this.collection.size;
  }
}

注意原本 Setsize 是一个属性,这里为了符合面向对象编程中属性私有、方法公有的原则,我们将 size 变为了一个函数

到这里主要的功能已经都实现了,但是还差一个遍历元素的方法。我们知道 Set 对象提供了 keysvaluesentries 三个迭代器,但实际上 keysentries 都和 values 一致,因此我们只要实现 values 就行:

class NewSet<T extends unknown> {
  private collection: Map<T, undefined>;

  constructor(iterable: T[] = []) {
    this.collection = new Map(
      iterable.map(it => [it, undefined])
    );
  }

  public add(value: T): NewSet<T> {
    this.collection.set(value, undefined);
    return this;
  }

  public delete(value: T): boolean {
    return this.collection.delete(value);
  }

  public has(value: T): boolean {
    return this.collection.has(value);
  }

  public size(): number {
    return this.collection.size;
  }

  public *values() {
    for (let [key,] of this.collection) {
      yield key;
    }
  }
}

注意 Map 的默认迭代器是 entries,即遍历的是每一个 entry。如果想直接遍历 key,可以使用 this.collection.keys()

当然迭代器除了自己实现,还有一种比较 hack 的方法是借用 map 的迭代器:

public values = this.collection.keys();

最后,我们知道 Set 对象是可迭代的,也就是可以被扩展运算符展开到数组中,或者可以使用 for...of 遍历。为了让我们实现的 Set 结构可迭代,我们需要实现 Symbol.iterator 接口,只要让它指向刚才实现的 values 方法即可,完整实现如下:

class NewSet<T extends unknown> {
  private collection: Map<T, undefined>;

  constructor(iterable: T[] = []) {
    this.collection = new Map(
      iterable.map(it => [it, undefined])
    );
  }

  public add(value: T): NewSet<T> {
    this.collection.set(value, undefined);
    return this;
  }

  public delete(value: T): boolean {
    return this.collection.delete(value);
  }

  public has(value: T): boolean {
    return this.collection.has(value);
  }

  public size(): number {
    return this.collection.size;
  }

  public *values() {
    for (let [key,] of this.collection) {
      yield key;
    }
  }

  public [Symbol.iterator] = this.values;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值