手动实现HashMap

什么是HashMap?

利用hash计算存储位置,存储键值对(Key-Value)的Map。

HashMap的结构

数组里面放链表,链表里面存储键值对

为什么这样设计?

数组的优势是查找效率高。
我们根据Key值计算出放入数组的位置,通过Key计算出的位置查找数据。
但是因为数组的大小是确定的,难以避免地会多个数据放到一个位置,此时我们就把放到数组同一个位置的数据设计成一个链表。(链表过长的话,还是需要扩容的)。

数据的结构

基本的数据:Key、Value,链表的指针,数组的下标

class Node<K,V>{
             Node<K,V> nextNode;//指向同一链表中下一个节点
             K k;//数据1
             V v;//数据2
             int hash;//在数组里的地址
}

数组的结构

private static int Capacity=10;
private Node[] listNodes=new Node[Capacity];

方法的实现,增、删、查(改)

增:put(K k,V v)

1、根据k计算下标
2、在数组对应位置查找是否已经有元素,如果为空直接放入
3、如果已经有元素,判断是否有相同的k值,如果有相同k值则进行修改,如果没有,则在链表末端放入该元素。

下标的计算

public int hash(K k) {
             return k.hashCode()%Capacity;
            //通过hashcode()计算得到的值不一定是符合数组的索引,取模运算
       }

泛型类k默认为Object,调用函数hashcode()得到一个int值,然后对数组容量进行取模,保证可以放入数组
而在HashMap源码中是通过与操作实现

if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

把数据包装成节点

Node node=new Node<>();
node.k=k;
node.v=v;

头插法在数组对应位置插入元素

Node node2=listNodes[index];
             if(node2!=null) {
                    node2=node.nextNode;
             }
             listNodes[index]=node;

以上两段代码可以优化为一个Node的构造函数

```java
Node node2=listNodes[index];
listNodes[index]=new Node(k,v,index,node2);
public Node(K k,V v,int hash,Node<K, V> node) {
                    this.k=k;
                    this.v=v;
                    this.hash=hash;
                    this.nextNode=node;
             }

遍历链表查找相同k值

```java
for(Node<K, V>node3=listNodes[index];node3!=null;node3=node3.nextNode) {
                    if(node3.k.equals(k)) {
                           node3.v=v;
                           return ;
                    }      
             }

查get(K k)

1、根据k值计算数组下标index
2、遍历数组该位置的链表

private V get(K k) {
             int index=hash(k);
             Node node=listNodes[index];
             while(node!=null) {
                    K k2=(K) node.k;
                    if(k2.equals(k)){
                           return (V) node.v;
                    }
                    node=node.nextNode;
             }
             return null;
       }

删remove(K k)

删除和get的方法区别不大,
1、根据k计算数组下标index
2、遍历链表listNodes(index)
3、找到匹配的k值,删除该节点

public void remove(K k) {
             int index=hash(k);
             Node node=listNodes[index];
            //如果要删除的值是第一个节点
             if(node.k.equals(k)) {
                    listNodes[index]=node.nextNode;
             }
                        //删除的值不是第一个节点,需要连接前一节点和后一节点
             if(node.nextNode!=null) {
                    K k2=(K) node.nextNode.k;
                    if(k2.equals(k)) {
                           node.nextNode=node.nextNode.nextNode;
                    }
                    node=node.nextNode;
             }
             return;
       }

扩容addd(int newCapacity)

在容量达到一定程度,链表的长度会很长,此时查找效率会降低,需要对数组进行扩容。

public void addd(int newCapacity) {//传入新的容量
        Node[]newlistNodes=new Node[newCapacity];//生成新的数组
             for (int i = 0; i < listNodes.length; i++) {//遍历原数组
                    Node<K, V> node = listNodes[i];//遍历原数组,
                    while(node!=null) {//遍历链表每个节点
                           Node<K, V> nextNode= node.nextNode;//暂存下一个节点
    
                            
                           int index=newhash(node.k);//给节点重新计算数组下标
                          
                            //头插法,把元素插入新的数组
                           node.nextNode=newlistNodes[index];//该节点指向数组
                           newlistNodes[index]=node;//数组中放节点

                           node=nextNode;//原链表的下个节点
                    }
             }
             listNodes=newlistNodes;
             Capacity=newCapacity;
       }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值