【数据结构】手写JavaScript版HashMap

本文主要介绍手写JavaScript版HashMap,侧重于HashMap数据结构的实现,并实现其基本api,保证是一个可用的HashMap。

同时也由于时间关系和语言翻译的出入,对于内存自增长这块没有体现,同时hash算法也采用的简洁版,后续有空再优化。

一、HashMap的重要性

首先,我们为什么要研究HashMap?

HashMap是Java中极其重要的数据结构,这种结构我们称之为散列链表。即元素是单链链表的数组。

1,对于数据库,如果存在千万条数据,我们的增删查改每次都要遍历一整个数据库,这将是灾难性的。对此我们可以用HashMap这种数据结构做索引优化

2,微信通讯录的设计,右侧有个abcd···序列表,可以提高我们找好友的效率,这也是HashMap数据结构的一个体现

二、jdk HashMap源码分析

查看jdk源码,我们看出HashMap的继承关系:

LinkedHashMap -> HashMap -> AbstractMap -> Map

其数据结构:

Object[entry<Key,Value>>
image-20220127202810611

java版用法。可以看出主要是对key value数据结构的一个保存

image-20220127202810611

同时提供api

size(大小)

isEmpty(判空)

containsKey(检查key)

containsValue(检查value)

get(查找数据)

put(存数据)

remove(删除数据)

putAll(存多个数据)

clear(清空)

等方法

其存储逻辑主要依赖于hash算法,根据key把数据存到对应数组的链表中去,以分组的方法提高运算效率

三、JavaScript版HashMap实现

3.1、定义数据结构

首先我们定义一个[Node,Node]这样的数据结构。

对于链表节点我们我们需要一个key、value,另外添加next指针。

class Node {
    constructor(key, value, next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }
}

对于数组部分,我们定义一个table数组,并初始化结点,同时定义一个数组个数hashMapSize

class HashMap {
    constructor() {
        this.table = [];
        this.hashMapSize = 0;
        for (let index = 0; index < DEFAULT_INITIAL_CAPACITY; index++) {
            this.table.push(null);
        }
    }
}
3.2、put存数据

对于put数据,首先我们需要查找存放到哪个数组

这里我们首先对key进行hash,并使用位运算或者取余运算映射到0~this.table.length的数组中去

const index = this.hash(key) & (this.table.length - 1);
// const index = this.hash(key) % this.table.length;

对于存数据,我们分两种情况,情况一,如果该数据在HashMap中已经有了,我们就修改value即可。

这里链表的遍历是依赖于next指针,直到为空

for (let headNode = this.table[index]; headNode != null; headNode = headNode.next) {
    if (this.hash(headNode.key) === this.hash(key) && headNode.key === key) {
        const oldValue = headNode.value;
        headNode.value = value;
        return oldValue;
    }
}

情况二:如果没有则直接存到该链表的数据中,并且数据size加一。

存的操作:新增一个节点next指向原来的头节点,然后新节点上则作为头节点

this.table[index] = new Node(key, value, this.table[index])
this.hashMapSize++;

对于其他操作则比较简单,这里不再赘述,看下源码即可

四、HashMap源码

class Node {
    constructor(key, value, next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }
}

// The default initial capacity - MUST be a power of two.
const DEFAULT_INITIAL_CAPACITY = 1 << 4;

class HashMap {
    constructor() {
        this.clear();
    }

    // 大小
    size() {
        return this.hashMapSize;
    }

    // 判空
    isEmpty() {
        return this.hashMapSize === 0;
    }

    // 包含key
    containsKey(key) {
        return this.get(key) != null;
    }

    // 包含value
    containsValue(value) {
        for (let index = 0; index < DEFAULT_INITIAL_CAPACITY; index++) {
            for (let headNode = this.table[index]; headNode != null; headNode = headNode.next) {
                if (headNode.value === value) {
                    return true;
                }
            }
        }
        return false;
    }

    // 根据key获取value
    get(key) {
        const index = this.hash(key) & (this.table.length - 1);
        for (let headNode = this.table[index]; headNode != null; headNode = headNode.next) {
            if (this.hash(headNode.key) === this.hash(key) && headNode.key === key) {
                return headNode.value;
            }
        }
        return null;
    }

    // 存键值对
    put(key, value) {
        const index = this.hash(key) & (this.table.length - 1);
        // const index = this.hash(key) % this.table.length;
        for (let headNode = this.table[index]; headNode != null; headNode = headNode.next) {
            if (this.hash(headNode.key) === this.hash(key) && headNode.key === key) {
                const oldValue = headNode.value;
                headNode.value = value;
                return oldValue;
            }
        }
        this.table[index] = new Node(key, value, this.table[index])
        this.hashMapSize++;
        return null;
    }

    // 删除
    remove(key) {
        const index = this.hash(key) & (this.table.length - 1);
        let headNode = this.table[index];
        if (headNode != null) {
            for (; headNode.next != null; headNode = headNode.next) {
                if (this.hash(headNode.next.key) === this.hash(key) && headNode.next.key === key) {
                    headNode.next = headNode.next.next;
                }
            }
        }
        return null;
    }

    // 存map
    putAll(map) {
        for (let index = 0; index < map.size(); index++) {
            for (let headNode = map[index]; headNode != null; headNode = headNode.next) {
                this.put(headNode.key, headNode.value);
            }
        }
    }

    // 清空
    clear() {
        this.table = [];
        this.hashMapSize = 0;
        for (let index = 0; index < DEFAULT_INITIAL_CAPACITY; index++) {
            this.table.push(null);
        }
    }

    // 打印所有数据
    toString() {
        let str = '{';
        for (let index = 0; index < DEFAULT_INITIAL_CAPACITY; index++) {
            for (let headNode = this.table[index]; headNode != null; headNode = headNode.next) {
                if (str.length !== 1) {
                    str += ',';
                }
                str += (headNode.key + '=' + headNode.value);
            }
        }
        console.log(str + '}');
    }

    hash(str){
        var hashCode=0
        for(var i=0;i<str.length;i++){
            hashCode=37* hashCode + str.charCodeAt(i) //获取编码
        }
        return hashCode
    }
}

五、验证结果

我们做存的操作,并打印出结果与数据结构:

const hashMap = new HashMap();
hashMap.put("张三", "18");
hashMap.put("李四", "19");
hashMap.put("王五", "20");
hashMap.put("赵六", "21");

hashMap.toString();
console.log(hashMap)
console.log(hashMap.get("张三"))
image-20220127202810611

这里验证正确,并且可以看到数据已经分散到各个组里面去了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流星雨在线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值