上章节,使用javascript简单实现了集合的数据结构。而此篇实现的字典,在结构上与上章的集合很相似,只不过两者存储的数据内容略有不同。然而,为什么会有这两种不同的数据结构呢?是因为字典获取值比较快吗?这个暂时还想不明白。但是对于散列表来说,它使用hashCode最为键来保存数据,而JavaScript语言内部,就是使用散列表来表示每个对象的。感觉这类似于数组取值的方法。本章节,我是带着问题来实现字典结构的,想不明白,为什么要分集合、字典两种?难道就是因为各自类具有不同的方法吗。。。一下就是本次的代码:

公共方法(字典与散列表公用)

// 格式化key
function setKeyFn(key) {
  if (key == null) return "null";
  if (key == undefined) return "undefined";
  if (typeof key == "string" || key instanceof String) return key;
  if (typeof key == "object") return JSON.stringify(key);
};
// 字典值
function ValueOrigin(key, value) {
  this.key = key;
  this.value = value;
}
字典数据结构实现
function CustomMap() {
  // 字典
  this.data = {};
  // 添加或替换字典项
  CustomMap.prototype.set = function (key, value) {
    if (key != null && value != null) {
      let _key = setKeyFn(key);
      let _value = new ValueOrigin(key, value);
      this.data[_key] = _value
      return true
    }
    return false
  }
  // 判断字典是否包含指定键
  CustomMap.prototype.hasKey = function (key) {
    let _key = setKeyFn(key);
    return !!this.data[_key]
  }
  // 删除指定key
  CustomMap.prototype.remove = function (key) {
    if (this.hasKey) {
      delete this.data[setKeyFn(key)];
      return true
    }
    return false
  }
  // 通过key,查找value
  CustomMap.prototype.get = function (key) {
    let target = this.data[setKeyFn(key)];
    return target
  }
  // 查找全部key和value
  CustomMap.prototype.keyValues = function () {
    return Object.values(this.data)
  }
  // 查找全部value
  CustomMap.prototype.values = function () {
    return this.keyValues().map(item => item.value)
  }
  // 查找全部key
  CustomMap.prototype.keys = function () {
    return this.keyValues().map(item => item.key)
  }
  // 迭代每个键值对
  CustomMap.prototype.forEach = function (cal) {
    let tmp = this.keyValues();
    for (let i = 0; i < tmp.length; i++) {
      let reslute = cal(tmp[i].value, tmp[i].key);
      if (reslute === false) break;
    }
  }
  // 获取字典的大小
  CustomMap.prototype.size = function () {
    return Object.keys(this.data).length
  }
  // 清空字典
  CustomMap.prototype.clear = function () {
    this.data = {};
  }
  // 是否为空字典
  CustomMap.prototype.isEmpty = function () {
    return !!this.size()
  }
}
let map = new CustomMap();
map.set("a", { id: 1 })
map.set("a1", { id: 2 })
let res = map.keyValues()
let res1 = map.keys()
let res2 = map.values()
let res3 = map.forEach((item, key) => {
  console.log(item, key)
  if(key == 'a') return false
})
console.log(map, res, res1, res2)

这便是字典的基本数据结构了,出来value保存的内容不同之外,是不是与集合很像?

散列表
// 散列表:
// 散列算法:尽可能的在数据中,快速找到一个值.
// 目标:给定一个键,返回对应的值所在的位置.
function HashTable() {
  this.data = {};
  // 新增或删除一个字典值
  HashTable.prototype.put = function (key, value) {
    if (key != null && value != null) {
      let pos = hashCode(key);
      this.data[pos] = new ValueOrigin(key, value);
    }
    return false
  }
  // 获取一个字典值
  HashTable.prototype.get = function (key) {
    return this.data[hashCode(key)]
  }
  // 删除一个字典值
  HashTable.prototype.remove = function (key) {
    let curHashCode = hashCode(key)
    if (this.data[curHashCode] !== undefined) {
      delete this.data[curHashCode]
      return true
    }
    return false
  }
}

let hashTable = new HashTable();
hashTable.put('javascript', { id: 1 })
hashTable.put('gao', { id: 1 })
hashTable.put('ji', { id: 1 })
hashTable.put('cheng', { id: 1 })
hashTable.put('xu', { id: 1 })
hashTable.put('she', { id: 1 })
hashTable.put('n', { id: 1 })
// let res = hashTable.get('a')
// let res1 = hashTable.remove('a')
console.log(hashTable)

// 散列函数:把每个键中的ASCII值相加
// A ~ Z:65 - 90
// a ~ z:97 - 122
function hashCode(key) {
  if (typeof key == 'number') return key;
  let likeHashKey = setKeyFn(key);
  // 此处也有冲突,但是很少
  let target = 5381;
  for (let i = 0; i < likeHashKey.length; i++) {
    target = target * 33 + likeHashKey.charCodeAt(i)
  }
  return target % 1013;
  // `javascript高级程序设n`
  // 下面的方法有冲突:gao -> xu 与 ji -> n
  // 解决冲突的三种办法:分离链接、线性探查、双散列法。这里不再实现
  // let target = 0;
  // for (let i = 0; i < likeHashKey.length; i++) {
  //   target = target  + likeHashKey.charCodeAt(i)
  // }
  // return target % 37;
}

上面便是散列表的实现,个人感觉蛮不错的。尤其对冲突平衡的解决,这里暂时没实现,后面会进行补充。附带代码中提到的冲突截图:

CoreJAVA 字典 javascript 字典_数据结构

另外,对于hashCode的功能,为什会如此?这是数学的魅力,又是谁发现的?待究…