JavaScript 数据结构之散列表的创建

目录
  • 一、什么是散列表
  • 二、创建散列表
    • 1.创建散列函数
    • 2.put 方法
    • 3.get 方法
    • 4.delete 方法
  • 三、使用散列表
  • 四、总结

散列表与字典基本一致,区别是字典存储的 key 是字符串,而散列表是一个数值(哈希值)。

到底如何理解散列表呢?下面进入正题。

一、什么是散列表

散列表,也叫做哈希表,可以根据键(Key)直接访问数据在内存中存储的位置。

简单来说,散列表就是字典的另一种实现,它的优势是比字典能更快地找到一个值。在常规的字典操作中,使用get()方法获得一个值,需要遍历整个数据结构,这样明显会比较慢。

散列表为了让查找提速,使用了一个叫散列函数的方法,将 key 转换成一个由 Unicode 码组合而成的数值,这个数值被称为散列值

最终在散列表中存储数据的结构是:散列值为 key,数据值为 value。这样查找数据时,就可以通过散列值直接定位位置,就好比数组下标一样直接定位元素,免去了整个数据结构的遍历,因此比字典的字符串定位要快上许多。

上述的概念如果比较难理解,看一张图你就明白了:

散列表还可以用来做数据库的索引。在关系型数据库如 MySQL 中,当你新建一张表并创建好了字段,你还可以为某些字段设置索引。设置索引是在散列表中存储了索引值和对应记录的引用,以便快速的找到数据。

当然了散列表还有其他应用,比如我们 JavaScript 当中的对象,那就是一个妥妥的散列表。

二、创建散列表

和字典类 Dictionary 一样,用一个对象来存储所有键值对。

class HashMap {
  constructor() {
    this.table = {}
  }
}

然后给类添加方法,主要是这三个:

  • put:向散列表增加/更新一个项
  • remove:根据键名移除键值
  • get:根据键名获取键值

当然还需要和上一篇一样的转换字符串函数:

function keyToString(item) {
  if(item === null) {
    return 'NULL'
  }
  if(item === undefined) {
    return 'UNDEFINED'
  }
  if(item instanceof String) {
    return `${item}`
  }
  return item.toString()
}

1.创建散列函数

散列函数就是开头说到的,将字符串转换为散列值的函数。

hashCode(key) {
  if(typeof key === 'number') {
    return key;
  }
  let tableKey = keyToString(key)
  let hash = 0;
  for(let i = 0; i < tableKey.length; i++) {
    hash += tableKey.charCodeAt(i)
  }
  return Math.ceil(hash / 20);
}

上述代码中,hashCode 接受一个 key 值,首先判断参数 key 是否是一个数值,如果是则直接返回。否则的话将 key 值转换为字符串。

结下来的逻辑是,定义一个 hash 变量为 0,然后循环字符串的长度。在循环体内通过 charCodeAt 方法获取每个字母对应的 Unicode 编码,并将结果累加。

最后一行,返回 Math.ceil(hash / 20) 的值,这是什么意思呢?

其实作用非常简单,就是为了避免 hash 值过大,然后才将它除以一个数值然后取整。这里用的 20,你也可以根据你的是实际情况决定数值范围,改用其他数值。

2.put 方法

现在我们有了自己的 hashCode 函数,下面来实现 put 方法。

put(key, value) {
  if(key !== null && value !== null) {
    let pos = this.hashCode(key)
    this.table[pos] = new ValuePair(key, value)
    return true;
  }
  return false;
}

put 方法与字典的 set 方法几乎一样,区别只是 table 的属性从 key 变成了 hash。这也是散列表与字典的不同之处,只需要确保 hash 唯一即可。

ValuePair 是上篇介绍的类,用来存储键值对。

3.get 方法

从散列表中获取一个值也很简单。

get(key) {
  let valuePair = this.table[this.hashCode(key)]
  return valuePair ? valuePair.value : undefined;
}

首先通过前面创建的 hashCode 方法获取到 key 的 hash 值,然后在 table 中获取这个 hash 有没有匹配的 value。如果有则返回 value,无则返回 undefined。

4.delete 方法

最后一个方法是从散列表中删除一个项:

remove(key) {
  let hash = this.hashCode(key)
  if(this.table[hash]) {
    delete this.table[hash]
    return true;
  }
  return false;
}

以上就是散列表的全部实现,下面我们来使用。

三、使用散列表

首先添加几个键值对:

var hashmap = new HashMap()
hashmap.put('name', '捷德')
hashmap.put('color', '红黑')
hashmap.put('father', '贝利亚')

console.log('name:', hashmap.hashCode('name')) // name:21
console.log('father:', hashmap.hashCode('father')) // father:32

我们用 hashCode 方法获取了 key 的 hash 值,是两个两位数的数字。

接着我们根据 key 获取 value:

console.log(hashmap.get("name")); // 捷德
console.log(hashmap.get("color")); // 红黑
console.log(hashmap.get("size")); // undefined

然后再删除一个 key:

console.log(hashmap.remove("color")); // true
console.log(hashmap.remove("size")); // false
console.log(hashmap.get("color")); // undefined

你看这三个方法在使用的过程中,和字典的效果几乎一致。我们在类内部实现的 hash 值,在使用类方法的时候是无感知的,只是内部数据存储的结构不同。

四、总结

本篇介绍了很常用的散列表数据结构,你学会了吗?散列表与字典很相似,了解他们的区别非常关键。

不过本篇实现的散列表还有一个异常情况,就是生成的散列值可能重复,这样就会出现覆盖的情况。下一篇,我们介绍如何处理散列值的冲突。

(0)

相关推荐

  • Go语言的数据结构转JSON

    目录 结构体转为 JSON 格式 接口转为 JSON 格式 Marshal() 函数的原型 总结 在日常工作中,除了需要从 JSON 转化为 Go 的数据结构.但往往相反的情况是:我们需要将数据以 JSON 字符串的形式发送到 Web 服务器.今天我们将学会如何从一个结构化数据编码为 JSON . Json(Javascript Object Nanotation)是一种数据交换格式,常用于前后端数据传输.任意一端将数据转换成json 字符串,另一端再将该字符串解析成相应的数据结构,如strin

  • JavaScript的Set数据结构详解

    目录 1. 什么是 Set 2. Set 构造函数 2.1) 数组 2.2) 字符串 2.3) arguments 2.4) NodeList 2.5)  Set 3. Set 的实例属性和方法 4. Set 的成员访问 5. Set 的注意事项 6. Set 的使用场景 总结 1. 什么是 Set Set 可以简单的看作是数学上的集合. 它是一系列无序,没有重复数值的数据集合. 2. Set 构造函数 对于 Set 的构造函数的参数,可以传递以下几种形式. 2.1) 数组 const s =

  • JS使用reduce()方法处理树形结构数据

    定义 reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值. reduce() 与forEach().map().filter()这些方法一样,也会对数组中的每一项进行遍历,但是reduce() 可以将遍历的前一个数组项产生的结果与当前遍历项进行运算. 语法 array.reduce(function(prev, cur, index, array){ ... }, init); 回调函数中的参数: prev 必需.表示调用回调时的返

  • JS散列表碰撞处理、开链法、HashTable散列示例

    本文实例讲述了JS散列表碰撞处理.开链法.HashTable散列.分享给大家供大家参考,具体如下: /** * 散列表碰撞处理.开链法.HashTable散列. * 将数组里的元素位置,也设置为数组,当两个数据的散列在同一个位置时, * 就可以放在这个位置的二维数组里,解决了散列函数的碰撞处理问题 */ function HashTable() { this.table = new Array(137); this.betterHash = betterHash;//散列函数 this.show

  • JavaScript中的Map数据结构详解

    目录 1. 什么是 Map 2. Map 构造函数 2.1) 数组 2.2) Set 2.3) Map 3. Map 的实例属性和方法 3.1) Map 的属性 size 3.2) Map 的方法 set get has delete clear forEach 4. Map的注意事项 5. Map的使用场景 总结 1. 什么是 Map Map 就是映射的意思,即从键到值的映射. Map 保存键值对,并且能够记住键的原始插入顺序. 那么它和 Object 有什么区别 ? 对象一般用字符串作键 c

  • JavaScript数据结构Number

    目录 前言: 一.NaN和Infinity 二.常用方法 1.安全数字 2.整数判断 3.数字格式判断 4.四舍五入 5.生成随机数 三.总结 前言: Number 是JavaScript的基本数据结构,是对应数值的应用类型.要创建一个 Number 对象,就使用 Number 构造函数并传入一个数值.在 JavaScript 中没有其他语言这么多的数字类型.根据 ECMAScript 标准,数字只有一种类型,它是“双精度 64 位二进制格式 IEEE 754 值”.这种类型用于存储整数和分数,

  •  JavaScript 数据结构之散列表的创建(2)

    目录 一.处理散列值冲突 1.分离链接 2.put 方法 3.get 方法 前言: 上一篇我们介绍了什么是散列表,并且用通俗的语言解析了散列表的存储结构,最后动手实现了一个散列表,相信大家对散列表已经不陌生了. 如果还不清楚散列表,请先阅读上一篇文章:JavaScript 数据结构之散列表的创建(1) 上篇末尾我们遗留了一个问题,就是将字符串转化为散列值后可能出现重复.当以散列值(hash 值)为 key 存储数据时,就会有覆盖已有数据的风险.本篇我们看如何处理散列值冲突的问题,并实现更完美的散

  •  JavaScript 数据结构之散列表的创建

    目录 一.什么是散列表 二.创建散列表 1.创建散列函数 2.put 方法 3.get 方法 4.delete 方法 三.使用散列表 四.总结 散列表与字典基本一致,区别是字典存储的 key 是字符串,而散列表是一个数值(哈希值). 到底如何理解散列表呢?下面进入正题. 一.什么是散列表 散列表,也叫做哈希表,可以根据键(Key)直接访问数据在内存中存储的位置. 简单来说,散列表就是字典的另一种实现,它的优势是比字典能更快地找到一个值.在常规的字典操作中,使用get()方法获得一个值,需要遍历整

  • Java数据结构之散列表(动力节点Java学院整理)

    基本概念 散列表(Hash table,也叫哈希表),是根据关键字(key value)而直接进行访问的数据结构. 说的具体点就是它通过吧key值映射到表中的一个位置来访问记录,从而加快查找的速度. 实现key值映射的函数就叫做散列函数 存放记录的数组就就叫做散列表 实现散列表的过程通常就称为散列(hashing),也就是常说的hash 散列 这里的散列的概念不仅限于数据结构了,在计算机科学领域中,散列-哈希是一种对信息的处理方法,通过某种特定的函数/算法(散列函数/hash()方法)将要检索的

  • Java数据结构之散列表详解

    目录 介绍 1 散列表概述 1.1 散列表概述 1.2 散列冲突(hash collision) 2 散列函数的选择 2.1 散列函数的要求 2.2 散列函数构造方法 3 散列冲突的解决 3.1 分离链接法 3.2 开放定址法 3.3 再散列法 4 散列表的简单实现 4.1 测试 介绍 本文详细介绍了散列表的概念.散列函数的选择.散列冲突的解决办法,并且最后提供了一种散列表的Java代码实现. 数组的特点是寻址容易,插入和删除困难:而链表的特点是寻址困难,插入和删除容易.而对于tree结构,它们

  • Javascript数据结构与算法之列表详解

    前言:在日常生活中,人们经常要使用列表,比如我们有时候要去购物时,为了购物时东西要买全,我们可以在去之前,列下要买的东西,这就要用的列表了,或者我们小时候上学那段时间,每次考完试后,学校都会列出这次考试成绩前十名的同学的排名及成绩单,等等这些都是列表的列子.我们计算机内也在使用列表,那么列表适合使用在什么地方呢?不适合使用在什么地方呢? 适合使用在:当列表的元素不是很多的情况下,可以使用列表,因为对列表中的元素查找或者排序时,效率还算非常高,反之:如果列表元素非常多的情况下,就不适合使用列表了.

  • JavaScript 数据结构之集合创建(2)

    目录 前言 一.集合运算 1.并集 2.交集 3.差集 4.子集 二.使用集合运算 三.总结 前言 上一篇JavaScript 数据结构 之集合创建(1)我们介绍了什么是集合,并且手动实现了一个集合的类.简单总结,集合就是一组元素唯一,并且没有顺序的数据集,关键是元素唯一. ES6 提供了原生的集合支持,就是新增的 Set 数据类型.其实在上篇我们几乎已经实现了 Set 的所有功能,如果还不了解集合,请看上篇内容 但是我们也说到,Set 的基本功能中不包含数学运算如 交集,并集,差集,事实上这也

  • JavaScript 数据结构 之集合创建

    目录 一.什么是集合 二.创建集合类 1.has 方法 2.add 方法 3.delete 和 clear 方法 4.size 方法 5.values 方法 三.使用集合 总结 前言: 集合这个词应该比较耳熟,大多数人没接触代码前就学过了.回想一下你的高一数学课本上是不是出现过这个词,就在第一章,概念如下: 一般地,我们把研究的对象统称为元素,把一些元素组成的总体叫作集合. 你看,集合,元素,是不是与今天我们学习的数据结构相通呢? 一.什么是集合 集合是由一组无序且唯一(不能重复)的元素组成.数

  • javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】

    本文实例讲述了javascript数据结构之多叉树经典操作.分享给大家供大家参考,具体如下: 多叉树可以实现复杂的数据结构的存储,通过遍历方法可以方便高效的查找数据,提高查找的效率,同时方便管理节点数据.javascript的DOM其实就是以多叉树的形式存储的.下面用javascript来实现多叉树的数据结构 1.创造一个节点 数据是以节点的形式存储的: class Node { constructor(data) { this.data = data; this.parent = null;

  • JavaScript数据结构学习之数组、栈与队列

    前言 数据结构就是关系,没错,就是数据元素相互之间存在的一种或多种特定关系的集合. 常用的数据结构有: 数组,队列(queue),堆(heap),栈(stack),链表(linked list ),树(tree),图(graph)和散列表(hash) 本文主要介绍的是数组.栈与队列,下面来一起看看详细的介绍吧. 一.数组 数组是平时使用最常用的数据结构,在JavaScript中数组是动态的分配大小,在这里我不会介绍JavaScript里面数组的所有的方法,而是针对数据结构这个方向谈谈所用到的方法

  • JavaScript数据结构之二叉查找树的定义与表示方法

    本文实例讲述了JavaScript数据结构之二叉查找树的定义与表示方法.分享给大家供大家参考,具体如下: 树是一种非线性的数据结构,以分层的方式存储数据.树被用来存储具有层级关系的数据,比如文件系统中的文件:树还被用来存储有序列表.这里将研究一种特殊的树:二叉树.选择树而不是那些基本的数据结构,是因为在二叉树上进行查找非常快(而在链表上查找则不是这样),为二叉树添加或删除元素也非常快(而对数组执行添加或删除操作则不是这样). 树是n个结点的有限集.最上面的为根,下面为根的子树.树的节点包含一个数

随机推荐