Java基础知识总结(17)

存放机制

1.初始化表格

默认情况构建HashMap时,如果表格table为Null,那么首先先new 一个table数组出来,里面存储的都是node

2.基于映射找到节点

存放时基于 key 和 value的键值对

首先基于key进行哈希函数的计算 将结果与table容量-1进行与运算 得到在table数组中的索引位置

3.添加或者更新节点

table数组中对应的位置有三种情况

对应位置为空---> 直接new Node() 把key ,value,哈希值,next全部封装到Node里面,然后把Node对象放到数组对应位置。

对应位置已经存放了Node对象,那么就遍历链表,如果在遍历过程中找到了hasCode和key值与添加节点相同,那么直接替换,如果没有找到相同的节点,那么直接将新节点添加到链表的末尾。在添加过程中,链表长度如果大于树化的阈值,树化的阈值默认为8,table的容量默认大于64时才能树化。容量没有达到64,要先扩容到64 然后再树化

对应位置是TreeNode ,那么就会基于红黑树的操作,将节点添加到红黑树里面,如果说红黑树里面已经key值相同的节点,就执行替换操作。

复制代码
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
​
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // i为数组下标  p为数组项中存放的链表
        HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
        //判断table是否为空,table对象是node数组,即hashmap中数据+链表+红黑树中的数组
        if ((tab = table) == null || (n = tab.length) == 0)
            //对hashmap的临界值和数组进行初始化
            n = (tab = resize()).length;
        //计算当前对象存放的下标,并判断数组中该下标是否已经存值
        if ((p = tab[i = (n - 1) & hash]) == null)
            ///如果不存在则在该位置创建一个新的链表。并将目标值存入新链表的表头
            tab[i] = newNode(hash, key, value, null);
        else {
            HashMap.Node<K,V> e; K k;
            //判断目标key的hash值与链表头节点的hash值是否相等,它们的key是否相等
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //判断p节点的类型是否是TreeNode
            else if (p instanceof HashMap.TreeNode)
                //如果红黑树节点中不存在与目标key相同的值,将目标对象插入红黑树中,存在相同的key则返回已存在的对象
                e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //遍历链表
                for (int binCount = 0; ; ++binCount) {
                    //到了链表尾部
                    if ((e = p.next) == null) {
                        //将目标对象插入链表尾部
                        p.next = newNode(hash, key, value, null);
                        //判断链表长度是否大于8
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //将链表转化为红黑树
                            treeifyBin(tab, hash);
                        break;
                    }
                    //遍历的过程中发现链表中有key与目标对象的key相同
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            //如果e不为空说明,hashmap中已经存在了当前目标对象的key,则将value进行覆盖
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                //修改值后,返回旧值
                return oldValue;
            }
        }
        //记录HashMap结构被修改的次数
        ++modCount;
        //判断数组大小是否已经大于临界值了
        if (++size > threshold)
            //进行扩容
            resize();
        afterNodeInsertion(evict);
        //Hashmap中不存在key,所以返回null
        return null;
    }
​
​
    final void treeifyBin(HashMap.Node<K,V>[] tab, int hash) {
        int n, index; HashMap.Node<K,V> e;
        //如果数组大小小于64
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            //进行扩容
            resize();
        //链表结构转红黑树
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            HashMap.TreeNode<K,V> hd = null, tl = null;
            do {
                HashMap.TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

4.超过阈值进行扩容

先添加再决定要不要扩容

整体表格的总元素数量超出阈值,对table进行扩容

扩容机制

扩容需要解决的问题?

1.计算新表格的大小,在原基础之上乘以2

2.遍历表、链、树中的元素,重新映射并拷贝至新位置

相关推荐
XuanXu12 分钟前
Java AQS原理以及应用
java
风象南3 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio12 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室17 小时前
java日常开发笔记和开发问题记录
java
咖啡教室17 小时前
java练习项目记录笔记
java
鱼樱前端17 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea18 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq
RainbowSea18 小时前
5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
java·消息队列·rabbitmq
我不会编程55520 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python