字典
字典是一种以键 - 值对应形势储存的数据结构,就像手机里面的电话本一样,只需要记录名字和对应的手机号码,下次拨打的时候只需要查找名字就可以了。这里键就是手机号的名字,而真正拨打的手机号是查找到的值。
在 ES6 中新增了一个原生的 Map 数据结构,参考 ES6 的 Map 数据结构实现一下。(JavaScript 也在发展)
- 建立字典类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| class Dictionary { constructor() { this.items = {}; } set(key, value) { this.items[key] = value; } remove(key) { if (this.has(key)) { delete this.items[key]; return "删除成功"; } return "删除失败"; } has(key) { return this.items.hasOwnProperty(key); } get(key) { return this.has(key) ? this.items[key] : undefinded; } clear() { this.items = {}; } size() { return Object.keys(this.items).length; } keys() { return Object.keys(this.items); } values() { let value = []; for (let i in this.items) { if (this.has(i)) { value.push(this.items[i]); } } return value; } }
const b = new Dictionary(); b.set(1, 1); b.set(2, 2); b.set(3, 3); b.set(4, 4); b.set(5, 5); b.get(5); b.size(); b.values(); b, has(5); b.remove(1); b.values();
|
哈希表也叫散列表,是根据键(key)而直接访问在内存存储位置的数据结构,也就是说,他通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问,加快了查找速度,这个映射函数称作散列函数,存放记录的数组称作散列表 —维基百科
ES6 新增的 Map 数据结构很适合用于哈希表,JavaScript 的对象 Object,本质上是键值对的结合(Hash 结构),但是传统上只能用字符串当做键,这给他的使用带来了很大的限制。
1 2 3
| const a = {}; a[1] = '233' a['1']
|
这里的数字 1 被转换成了字符串 1,本意是将一个数字 1 传入对象,但是 Object 只接受字符串所以被自动转换了。
为了解决这个问题,ES6 提供了 Map 数据结构,他类似于对象,也是键值对的集合,但是键的范围不限于字符串,各种类型的值(包括对象)都可以当做键,也就是说 Object 提供了字符串 - 值的对应,而 Map 结构提供了值 - 值的对应,是一种更加完善的 Hash 结构实现 — 阮一峰 ES6
用 class 实现一个表
charCodeAt用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class HashTable { constructor() { this.table = []; } getHashTableCode(key) { let Hash = 0; for (let i = 0; i < key.length; i++) { Hash += key.charCodeAt(i); } console.log(Hash); return Hash % 37; } put(key, value) { const position = this.getHashTableCode(key); this.table[position] = value; } get(key) { const position = this.getHashTableCode(key); return this.table[position]; } remove(key) { const position = this.getHashTableCode(key); table[position] = void 0; } } let hashTable = new HashTable(); hashTable.put("Mark", "mark@gmail.com"); hashTable.put("Ivy", "ivy@gmail.com"); hashTable.put("Mary", "mary@gmail.com");
hashTable.get("Mark"); hashTable.get("Jack"); hashTable.remove("Mark"); hashTable.get("Mark");
|
在上面实际的过程中,会有一个问题,就是可能会有不同的键值但是拥有相同的 hash 值的情况出现比如 Jamie 和 Sue 两个字符串获得的 hash 值就会相同。
1 2 3 4 5 6 7 8 9 10 11 12
| getHashTableCode(key){ let Hash = 0; for(let i = 0; i < key.length; i++) { Hash += key.charCodeAt(i); } console.log(Hash) return Hash % 37; } getHashTableCode('Jamie') getHashTableCode('Sue')
|
若这种情况不处理,那么后来的值就会覆盖前面的值
1 2 3 4 5 6 7 8 9 10 11
| getHashTableCode(key){ let Hash = 5381; for(let i = 0; i < key.length; i++) { hash = hash * 33 + key.charCodeAt(i); } console.log(Hash) return hash % 1013; }
|
接下来介绍一下 ES6 的 Map 数据结构
Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。 —看的阮一峰的在线书
size 属性
size
属性返回 Map 结构的成员总数
1 2 3 4 5
| const map = new Map(); map.set("foo", true); map.set("bar", false);
map.size;
|
set(key, value)
set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。
1 2 3 4 5
| const m = new Map();
m.set("edition", 6); m.set(262, "standard"); m.set(undefined, "nah");
|
set
方法返回的是当前的Map
对象,因此可以采用链式写法。
1 2 3 4
| let map = new Map() .set(1, "a") .set(2, "b") .set(3, "c");
|
get(key)
get
方法读取key
对应的键值,如果找不到key
,返回undefined
。
1 2 3 4 5 6 7 8
| const m = new Map();
const hello = function() { console.log("hello"); }; m.set(hello, "Hello ES6!");
m.get(hello);
|
has(key)
has
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
1 2 3 4 5 6 7 8 9 10
| const m = new Map();
m.set("edition", 6); m.set(262, "standard"); m.set(undefined, "nah");
m.has("edition"); m.has("years"); m.has(262); m.has(undefined);
|
delete(key)
delete
方法删除某个键,返回true
。如果删除失败,返回false
。
1 2 3 4 5 6
| const m = new Map(); m.set(undefined, "nah"); m.has(undefined);
m.delete(undefined); m.has(undefined);
|
clear()
clear
方法清除所有成员,没有返回值。
1 2 3 4 5 6 7
| let map = new Map(); map.set("foo", true); map.set("bar", false);
map.size; map.clear(); map.size;
|
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回所有成员的遍历器。
forEach()
:遍历 Map 的所有成员。
需要特别注意的是,Map 的遍历顺序就是插入顺序。