ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
我们先使用下Map
let map = new Map()
map.set(1, 'a')
map.set(2, 'b')
map.set(3, 'c')
console.log(map) //
// [[Entries]]
// [{1 => "a"}
// {2 => "b"}
// {3 => "c"}]
console.log(map.get(1)) //a
console.log(map.has(2)) // true
console.log(map.has(9)) // false
map.delete(1) // 删除1
我们打印出来的 Map 是一个键值对组成的伪数组,所以我们就可以这样
遍历
for (let entries of map) {
console.log(entries)
/** (2) [1, 'a']
(2) [2, 'b']
(2) [3, 'c']*/
}
转数组
Array.from(map) // [[1, 'a'],[2, 'b'],[3, 'c']]
转对象
Object.fromEntries(map) // {1: 'a', 2: 'b', 3: 'c'}
Map原理
在 Object 中, key 必须是简单数据类型(整数,字符串或者是 symbol),而在 Map 中则可以是 JavaScript 支持的所有数据类型,也就是说可以用一个 Object 来当做一个Map元素的 key。
Map存值会通过hash算法,根据不同的键放到数组的8个不同的房间(因为字节的最小单位 1byte = 8 bit)。每个房间里面是一个链表,新增,删除会快一点。也就是说Map相对于Object来说,会比Object操作快一些(其实快的也不是很多,数据不是很多可以忽略)。
手写实现代码
// 手写Map
class MyMap {
constructor() {
this.count = 8
this.store = []
this.initStore()
}
// 初始化
initStore() {
// 每个房间是一个链表,所以先创建一个表头
for (let i = 0; i < 8; i++) {
this.store[i] = {
next: null
}
}
}
// 修改
set(k, v) {
// 通过K获取房间号
let index = this.hash(k)
// 获取标头
let top = this.store[index]
// 判断有没有这个k, 有替换 没有修改
while (top.next) {
if (top.key === k) {
top.value = v
} else {
// 如果没有找到就 下一个
top = top.next
}
}
// 如果没有找到 就是新增
top.next = {
next: null,
key: k,
value: v
}
}
// 获取
get(k) {
// 通过K获取房间号
let index = this.hash(k)
// 获取标头
let top = this.store[index].next // 跳过表头
while (top) {
if (top.key === k) {
return top.value
} else {
// 如果没有找到就 下一个
top = top.next
}
}
// 如果没有找到就
return undefined
}
// 根据键去匹配房间,底层不是这么简单,我们这里简单处理下
hash(k) {
return k % this.count
}
delet(k){
// 以后加
}
// 是否存在
has(k) {
return this.get(k) === !undefined
}
}
let map = new MyMap()
map.set(1, "a")
map.set(2, "b")
map.set(3, "c")
map.set(4, "d")
map.set(5, "e")
map.set(6, "f")
map.set(7, "g")
map.set(8, "h")
map.set(9, "i")
console.log(map.get(2)) // b