HashMap底层数据结构

提示:以下是本篇文章正文内容,下面案例可供参考

一、HashMap底层数据结构是什么?

示例:JDK1.8,HashMap底层数据结构是基于数组 + 链表 + 红黑树(链表长度达到指定长度 8 后,转成红黑树处理哈希碰撞问题)

二、使用步骤

1.put( key,value)


import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName HashMapDemo
 * @Description TODO
 * @Author ZCH
 * @Date 2024/9/4 15:49
 */
public class HashMapDemo {

    public static void main(String[] args) {

        //初始容量  +  加载因子
        Map<String, Integer> map2 = new HashMap<>(16,0.75F);
        map2.put("putonekey", 40);
        map2.put("putonekey", 50);
        map2.clear();

    }
}



    V put(K key, V value);

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

2.hash(key) 用hash算法计算出key的hash值

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        //将 key 进行一个 hashCode计算得到 key的 hashCode值
        //再拿hashCode值的高16位  异或  低16位 最终得到一个hash值 
    }

3.putVal(hash(key), key, value, false, true)


    final V putVal(
				    int hash,  //key 的 hash值 👆上面计算出来的值
				    K key, //存入的key
				    V value, //存入的value
				    boolean onlyIfAbsent, // 是否使用了 putIfAbsent(key,value)方法标识
				    boolean evict //是否链表头部插入节点 默认 true
				   ) 
   {
        Node<K,V>[] tab; //数组 元素是entry对象类型的节点  键值对。
        Node<K,V> p; //当前位置所在的节点对象 键值对
        int n, i; // n数组长度  i 数组的索引值
        //数组为空时,通过 扩容方法 来初始化数组长度
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            
        // hash值 & 数组长度-1 得到数组索引下标 上的节点 即当前位置上的节点 
        //数组索引位置上的节点若为null,则在这个数组索引位置上创建一个新的节点   
        //若存入的key计算出来的数组索引下标上没有节点时,就在这个定位上创建一个节点
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
            
        else {
        
            Node<K,V> e;  //即将存储的节点元素
            K k; //存入的key
            //存入的key的hash值和当前定位上的节点的hash值相等 key值也相等
            //意思就是 当前存入的key和当前节点上的key是同一个key 即若存入的key相同,则覆盖节点 把节点原封不动给了 新节点
            if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
                
            else if (p instanceof TreeNode) //若数组索引位置上的节点 是 红黑树的实例 则证明 这个位置上,之前,曾经,历史上发生过很多次哈希碰撞,已经达到了链表转成红黑树的条件,那么现在即将存入的key就存到 红黑树上去
            
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else { //不是同一个key,也不是红黑树,那就是普普通通的正常存储 数组+链表
           
            // 若索引位置上的节点有下一个节点,链表长度+1
                for (int binCount = 0; ; ++binCount) {
          
                //若索引位置上的节点没有下一个节点,创建一个新的节点放在 索引位置上的节点的后面,并判断此时链表的长度是否达到转红黑树的要求
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //判断此时链表的长度是否达到转红黑树的要求
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        //转成树结构存储
                            treeifyBin(tab, hash);
                        break;
                    }
                    //
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                   //若索引位置上的节点有下一个节点,则将索引位置上节点的下一个节点  继续进行循环...找到索引位置上的下下个节点,同时将链表长度+1 ,继续找下一个节点,直到链表的最后一个节点...没有下一个节点了,创建一个新的节点放在链表的最后面
                    p = e;
                }
            }
            //重新赋值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null) // 是否使用了 putIfAbsent(key,value)方法标识
                //把新值 赋值给新节点
                    e.value = value;
                afterNodeAccess(e);
                //put方法的返回值是 返回原来的值
                return oldValue;
            }
        }
        //并发修改数
        ++modCount;
        //键值对数量 大于 扩容阈值时 ,则调用扩容方法进行扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

总结

1.将 key 进行一个 hashCode计算得到 key的 hashCode值,再拿key的hashCode值的高16位 异或 低16位 最终得到一个hash值
2.将经过hash算法计算得到的hash值 和 一个固定数组长度 进行一个 & 的运算 得到一个坐标 哈希桶数组的index
3. 根据上面计算得到的index 去获取 node节点:
如果index位置上没有Node 根据hash值创建一个节点
如果index位置上有节点: 1.是否有相同key,有相同key则将当前节点重新赋值返回;
2.当前节点是否经过哈希碰撞后产生了红黑树,如果产生了红黑树,加入红黑树;
3.如果不是相同key也不是红黑树,就在当前节点进行挖掘,挖呀挖呀,一挖一个不吱声,如果能挖到超过八米深,则种一棵红黑树;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值