zoukankan      html  css  js  c++  java
  • HashMap源码分析

    一 简介

    Map接口的基于散列表的一种实现,和Hashtable大致相同。但是支持null键和null值。非线程安全。
    HashMap无法保证元素的顺序,特别是,它不能保证顺序会随着时间的推移保持恒定。

    二 类继承关系

    在这里插入图片描述
    可被序列化,可被克隆,继承AbstractMap抽象类,实现Map接口

    三 属性

    //默认初始容量
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //负载因子 当达到最大容量的多少时扩容
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //最小树形化容量阈值  转换为树型存储的阈值 >8
    static final int TREEIFY_THRESHOLD = 8;
    //桶的链表还原阈值  树型转换为普通存储的阈值 <6
    static final int UNTREEIFY_THRESHOLD = 6;
    //最小树形化容量阈值  只有hash槽位大于 64 才会发生转换
    static final int MIN_TREEIFY_CAPACITY = 64;
    //该表在首次使用时初始化,并根据需要调整大小。分配后,长度始终是2的幂
    transient Node<K,V>[] table;
    //缓存entrySet()的值
    transient Set<Map.Entry<K,V>> entrySet;
    //长度 键值对数量
    transient int size;
    //对该HashMap进行结构修改的次数
    transient int modCount;
    //阈值 下一个要调整大小的大小值(容量*负载系数)
    int threshold;
    //负载因子
    final float loadFactor;
    

    四 HashMap内部的两类节点

    //Hashmap中的节点封装 是一个单向链表(当hash冲突时放到当前链表后面 就是人们说的:拉链法)
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
    }
    //hashmap当hash冲突比较多时 将链表转换为红黑树
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
    }
    

    五 构造方法

    public HashMap() {
       this.loadFactor = DEFAULT_LOAD_FACTOR; // 全部使用默认值
    }
    
    public HashMap(int initialCapacity) {//设定初始容量
      this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
    public HashMap(int initialCapacity, float loadFactor) {//设定初始容量和负载因子
    	//检查初始容量
        if (initialCapacity < 0)
           throw new IllegalArgumentException("Illegal..." + initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        //检查负载因子
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
           throw new IllegalArgumentException("Illegal..." + loadFactor);
        this.loadFactor = loadFactor;
        //计算扩容阈值
        this.threshold = tableSizeFor(initialCapacity);
    }
    
    //返回输入值最近的2的n次方
    static final int tableSizeFor(int cap) {
        int n = cap - 1;//防止正好等于2的n次方时 扩容
        n |= n >>> 1; // n‘或’ n无符号右移一位 比如:n=6(0110)右移1 0011 或 0110= 0111
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    //这个算法很有趣
    例如 cap = 5 二进制 0101
    n = cap -1 		n=40100)	防止正好等于2的n次方时扩容
    n |= n >>> 1	n右移1位(0010)与自己或 0100|0010=0110
    n |= n >>> 2	n右移2位(0001)与自己或 0110|0001=0111
    n |= n >>> 4	n右移4位(0000)与自己或 0111|0000=0111
    n |= n >>> 8	n右移8位(0000)与自己或 0111|0000=0111
    n |= n >>> 16	n右移16位(0000)与自己或 0111|0000=0111(十进制7)
    
    位移为了将第一个1往后移动 再与自己或后 
    有两点可以确定 1:保留最高位的1 2:最高位后面一位变为1
    
    随后往后位移2位  结果可以确定 保留高位后的后4位为1
    
    可以看到算法的结果为:将数字的二进制从第一个有效数字后0全变为1 
    此时再加1就是2的n次方了
    
    看似这么麻烦,其实就是这个意思 比如给你个数字:0100 1011 将它变为 0111 1111
    巧妙的借助了2的n次方数字的特性 还有移位运算的快速
    

    六 添加数据

    1 put

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    /**
     * @param hash 键的hash码
     * @param key 键
     * @param value 值
     * @param onlyIfAbsent true当存在时不做变更
     * @param evict false该表处于创建模式
     * @return 返回已经存在的那个值 如果没有返回null
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
        Node<K,V>[] tab; //指向当前hash表
        Node<K,V> p; //保存已经存在的键 和要插入的键相同
        int n; //hash表长度
        int i; //要插入到的hash槽
        //源码喜欢这种形式(tab=table)== null 一行代码做了两件事 赋值和比较 比较简洁
        //如果表为空 说明还没初始化 调用resize()调整大小
        if ((tab = table) == null || (n = tab.length) == 0) 
            n = (tab = resize()).length;
        //这里计算数据要插入的槽位 注意:(n - 1) & hash 数组长度减一和hash码相与
        //这里的算法 是为了取到一个0到2的n次方的数字 这样就能放到数组
        //比如:长度2的3次方=8(1000)-1 =(0111)& hash(1011)得到 0011 也就是3 
        //其精髓就是:截掉高位 保留低位
        if ((p = tab[i = (n - 1) & hash]) == null)
        	//如果槽位为空 也就是没有hash冲突 直接插入
            tab[i] = newNode(hash, key, value, null);
        //下面就要处理冲突了
        else {
            Node<K,V> e; K k;
            //如果槽位上那个节点正好和当前要插入的相等 那么将这个节点的值替换了就行
            //注意:两个节点相等是 hash码相等并且key相等
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //如果节点不和我们要插入的相等  并且是红黑树 那么就插入红黑树
            else if (p instanceof TreeNode)
                e = ((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);
                        //插入后判断是否要转换位红黑树
                        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;
                    p = e;
                }
            }
            //如果找到了相同的节点 替换旧的值就行
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //长度加一 并判断是否需要扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    
    static final int hash(Object key) {
    	int h;
    	//异或 是为了将高位的变化 扩展到地位 更好的混淆hash 避免hash冲突
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    

    2 红黑树插入putTreeVal

    /**
     * 树形版本的putVal
     * map当前map的引用
     * tab当前map里面的节点数组
     * h key的hash码
     * k 键
     * v 值
     * @return 返回被替换的重复节点 如果没重复 返回null
     */
    final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) {
        Class<?> kc = null;
        boolean searched = false;//是否检查过重复了
        //注意调用此方法的是一个树节点 如果没有parent说明自己是根 否者找根
        TreeNode<K,V> root = (parent != null) ? root() : this;
        //二分查找节点需要插入的位置
        for (TreeNode<K,V> p = root;;) {
            int dir, ph; K pk;
            //根据hash码和当前节点比较大小
            if ((ph = p.hash) > h)
                dir = -1;
            else if (ph < h)
                dir = 1;
            //如果hash码相等 并且key相等 直接返回当前节点
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                return p;
            //如果hash码相等 但是key不相等 说明存在hash碰撞
            //判断是否key能比较 能比较就试着找到左右子树中那个重复的节点
            else if ((kc == null &&
            		  //如果键实现了Comparable接口排序 返回key的类 否则空
                      (kc = comparableClassFor(k)) == null) || 
                      //如果两个节点类型不同 返回0 否者返回key的compar()结果
                     (dir = compareComparables(kc, k, pk)) == 0) {
                //从左右子树中找 重复的节点 找到直接返回
                if (!searched) {
                    TreeNode<K,V> q, ch;
                    searched = true;
                    if (((ch = p.left) != null &&
                         (q = ch.find(h, k, kc)) != null) ||
                        ((ch = p.right) != null &&
                         (q = ch.find(h, k, kc)) != null))
                        return q;
                }
                //根据两个节点键的'存储地址'比较大小
                dir = tieBreakOrder(k, pk);
            }
    
    		//检索到树叶 要插入的位置不为空 继续循环 为空就构造新节点放上去
    		//这里插入可以看到HashMap红黑树的特殊性:不但具有红黑树的特性 还有链表的特性
    		//left、right树的特性 parent、next链表特性 注意看下面代码
            TreeNode<K,V> xp = p;
            if ((p = (dir <= 0) ? p.left : p.right) == null) {
                Node<K,V> xpn = xp.next;
                TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
                if (dir <= 0)
                    xp.left = x;
                else
                    xp.right = x;
                xp.next = x;
                x.parent = x.prev = xp;
                if (xpn != null)
                    ((TreeNode<K,V>)xpn).prev = x;
                //重新平衡红黑树
                moveRootToFront(tab, balanceInsertion(root, x));
                return null;
            }
        }
    }
    

    七 扩容

    1 resize

    //初始化或者扩容
    final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;//旧表
        int oldCap = (oldTab == null) ? 0 : oldTab.length;//旧容量
        int oldThr = threshold;//旧的阈值
        int newCap, newThr = 0;
        if (oldCap > 0) {
        	//如果容量大于最大值 设置阈值为最大值即可
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // 将容量和阈值扩容一倍
        }
        //旧容量等于0 旧阈值大于0(使用非默认构造创建 例如:new HashMap(0))
        else if (oldThr > 0) // 初始化容量为阈值
            newCap = oldThr;
        else { //容量、阈值为空 说明使用默认构造创建(new HashMap())赋给默认值
            newCap = DEFAULT_INITIAL_CAPACITY;//默认容量
            //默认阈值=默认加载因子*默认容量
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;//修改阈值
        
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//创建新容量的桶
        table = newTab;//酒瓶换新桶
        if (oldTab != null) { //如果不是初始化 要遍历旧桶数据放入新桶
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                    	//桶中就一个单节点 根据hash码 计算在新桶位置
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode) //调用红黑树拆分方法 下面会讲
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { //拆分链表 分别放入低位(i=新桶原位置)和高位(i=原位置+旧容量)
                    	//拆出来要放入低位的链表头、尾引用
                        Node<K,V> loHead = null, loTail = null;
                        //拆出来要放入高位的链表头、尾引用
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            //注意:不是 e.hash & oldCap - 1
                            //这里又是一处非常巧妙位运算 
                            //再次利用了 2的n方 数字的特点(只有一位为1 比如:4二进位0100)
                            //hash码&过之后 只有0和1两个结果 以此判断放入高位低位
                            //其实像上面那样做:e.hash & (newCap - 1)
                            //结果也只会出来两个 分别是低、高位下标 算出来后分别插入新表也可行
                            if ((e.hash & oldCap) == 0) {
                            	//第一个将头尾指向它
                                if (loTail == null)
                                    loHead = e;
                                else
                                	//将新节点放到链表最后
                                    loTail.next = e; 
                                //尾节点指向这个新的尾节点
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        //拆分后的两个链表
                        //低位链表 放到 原来的位置
                        //高位链表 放到 原来的位置+旧容量
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    

    2 红黑树拆分split

    /**
     * 将树箱中的节点分为上下树箱,如果拆分后太小,则取消树化
     * @param map the map
     * @param tab 新桶
     * @param index 当前链表在旧桶中位置
     * @param bit 旧桶的容量
     */
    final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
        TreeNode<K,V> b = this;//要拆分的树的root节点
        //重新链接到lo和hi列表,保留顺序
        TreeNode<K,V> loHead = null, loTail = null;
        TreeNode<K,V> hiHead = null, hiTail = null;
        int lc = 0, hc = 0;
        for (TreeNode<K,V> e = b, next; e != null; e = next) {
            next = (TreeNode<K,V>)e.next;
            e.next = null;
            //和链表拆分同样的道理 分到两个链表(插入尾部)
            //有意思的是 这里把红黑树当作链表处理了
            if ((e.hash & bit) == 0) {
                if ((e.prev = loTail) == null)
                    loHead = e;
                else
                    loTail.next = e;
                loTail = e;
                ++lc;
            }
            else {
                if ((e.prev = hiTail) == null)
                    hiHead = e;
                else
                    hiTail.next = e;
                hiTail = e;
                ++hc;
            }
        }
    
    	//要根据数量 判断是否要对两个链表树化还是非树化
        if (loHead != null) {
            if (lc <= UNTREEIFY_THRESHOLD)
                tab[index] = loHead.untreeify(map);
            else {
                tab[index] = loHead;
                if (hiHead != null) // (else is already treeified)
                    loHead.treeify(tab);
            }
        }
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map);
            else {
                tab[index + bit] = hiHead;
                if (loHead != null)
                    hiHead.treeify(tab);
            }
        }
    }
    

    八 将Node链表树化

    1 treeifyBin

    //将某个hash槽位树化
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        //如果没有达到树化容量 就只是扩容
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
        	//将节点替换为TreeNode’链表‘
            TreeNode<K,V> hd = null, tl = null;
            do {
            	//将Node替换为TreeNode
                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); //这一步才是去树化 刚才只是转换了节点类型
        }
    }
    

    2 红黑树 树化treeify

    //可能传入的是树 也可能是链表
    final void treeify(Node<K,V>[] tab) {
        TreeNode<K,V> root = null;
        for (TreeNode<K,V> x = this, next; x != null; x = next) {
        	//第一个节点做根 并清空它的引用
            next = (TreeNode<K,V>)x.next;
            x.left = x.right = null;
            if (root == null) {
                x.parent = null;
                x.red = false;
                root = x;
            }
            else {
                K k = x.key;
                int h = x.hash;
                Class<?> kc = null;
                //将节点放到 红黑树中 从根节点开始比较
                for (TreeNode<K,V> p = root;;) {
                    int dir, ph;
                    K pk = p.key;
                    if ((ph = p.hash) > h)
                        dir = -1;
                    else if (ph < h)
                        dir = 1;
                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0)
                        dir = tieBreakOrder(k, pk);
    				//找到左右节点为空的就是要插入的位置
                    TreeNode<K,V> xp = p;
                    if ((p = (dir <= 0) ? p.left : p.right) == null) {
                        x.parent = xp;
                        if (dir <= 0)
                            xp.left = x;
                        else
                            xp.right = x;
                        root = balanceInsertion(root, x);//重新平衡红黑树
                        break;
                    }
                }
            }
        }
        moveRootToFront(tab, root);
    }
    

    3 树转换为链表untreeify

    final Node<K,V> untreeify(HashMap<K,V> map) {
        Node<K,V> hd = null, tl = null;//链表的头和尾
        for (Node<K,V> q = this; q != null; q = q.next) {
            Node<K,V> p = map.replacementNode(q, null);//构建节点 去除树的属性
            if (tl == null)
                hd = p;
            else
                tl.next = p;
            tl = p;
        }
        return hd;
    }
    
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
       return new Node<>(p.hash, p.key, p.value, next);
    }
    

    九 获取

    1 get

    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //(n - 1) & hash] hash码和长度-1取余找到所在的槽位
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && //检查第一个节点是我们要找的吗
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
            	//如果是红黑树 调用红黑树方法取 否则遍历链表呗
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
    

    2 树型版本的获取getTreeNode

    final TreeNode<K,V> getTreeNode(int h, Object k) {
        return ((parent != null) ? root() : this).find(h, k, null);
    }
    //从跟节点开始查找
    final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
         TreeNode<K,V> p = this;
         do {
         	//遍历红黑树找到节点
             int ph, dir; K pk;
             TreeNode<K,V> pl = p.left, pr = p.right, q;
             if ((ph = p.hash) > h)
                 p = pl;
             else if (ph < h)
                 p = pr;
             else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                 return p;
             else if (pl == null)
                 p = pr;
             else if (pr == null)
                 p = pl;
             else if ((kc != null ||
                       (kc = comparableClassFor(k)) != null) &&
                      (dir = compareComparables(kc, k, pk)) != 0)
                 p = (dir < 0) ? pl : pr;
             else if ((q = pr.find(h, k, kc)) != null)
                 return q;
             else
                 p = pl;
         } while (p != null);
         return null;
     }
    

    十 移除

    1 remove

    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
    
    final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        //删除之前需要先找到这个节点 查找过程和get一致
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //找到后 如果是链表将前面节点指向后面节点就行 如果是树 调用树的删除
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }
    

    2 树的移除removeTreeNode

    final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                             boolean movable) {
        int n;
        if (tab == null || (n = tab.length) == 0)
            return;
        int index = (n - 1) & hash;
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
        TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
        if (pred == null)//没有前驱节点 说明他是首节点
            tab[index] = first = succ;//将它后面节点放到槽位
        else//中间节点
            pred.next = succ;//将前一个节点指向后一个节点
        if (succ != null)
            succ.prev = pred;//后一个节点赋值给前一个节点
        if (first == null)
            return;
        if (root.parent != null)
            root = root.root();
        if (root == null// 删除后需不需要非树化
            || (movable
                && (root.right == null
                    || (rl = root.left) == null
                    || rl.left == null))) {
            tab[index] = first.untreeify(map);  
            return;
        }
        //下面是删除节点在红黑树中的引用 和上面删除在链表中引用 相辅相成
        TreeNode<K,V> p = this, pl = left, pr = right, replacement;
        if (pl != null && pr != null) {
            TreeNode<K,V> s = pr, sl;
            while ((sl = s.left) != null) // find successor
                s = sl;
            boolean c = s.red; s.red = p.red; p.red = c; // swap colors
            TreeNode<K,V> sr = s.right;
            TreeNode<K,V> pp = p.parent;
            if (s == pr) { // p was s's direct parent
                p.parent = s;
                s.right = p;
            }
            else {
                TreeNode<K,V> sp = s.parent;
                if ((p.parent = sp) != null) {
                    if (s == sp.left)
                        sp.left = p;
                    else
                        sp.right = p;
                }
                if ((s.right = pr) != null)
                    pr.parent = s;
            }
            p.left = null;
            if ((p.right = sr) != null)
                sr.parent = p;
            if ((s.left = pl) != null)
                pl.parent = s;
            if ((s.parent = pp) == null)
                root = s;
            else if (p == pp.left)
                pp.left = s;
            else
                pp.right = s;
            if (sr != null)
                replacement = sr;
            else
                replacement = p;
        }
        else if (pl != null)
            replacement = pl;
        else if (pr != null)
            replacement = pr;
        else
            replacement = p;
        if (replacement != p) {
            TreeNode<K,V> pp = replacement.parent = p.parent;
            if (pp == null)
                root = replacement;
            else if (p == pp.left)
                pp.left = replacement;
            else
                pp.right = replacement;
            p.left = p.right = p.parent = null;
        }
    
        TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
    
        if (replacement == p) {  // detach
            TreeNode<K,V> pp = p.parent;
            p.parent = null;
            if (pp != null) {
                if (p == pp.left)
                    pp.left = null;
                else if (p == pp.right)
                    pp.right = null;
            }
        }
        if (movable)
            moveRootToFront(tab, r);
    }
    

    总结

    HashMap 内部采用 数组+链表+红黑树 的数据结构
    默认容量16、加载因子0.75、阈值12
    扩容变为原来的两倍,大于最大值了就不扩容了
    HashMap非线程安全
    查找添加删除 复杂度O9(1)
    当桶的数量小于64时不会进行树化,只会扩容
    当桶的数量大于64且单个桶中元素的数量大于8时,进行树化
    当单个桶中元素数量小于6时,进行反树化

  • 相关阅读:
    51 Nod 1068 Bash游戏v3
    51 Nod Bash 游戏v2
    51 Nod 1073 约瑟夫环
    UVA 12063 Zeros and ones 一道需要好好体会的好题
    51 Nod 1161 Partial sums
    2018中国大学生程序设计竞赛
    UVA 11971 Polygon
    UVA 10900 So do you want to be a 2^n-aire?
    UVA 11346 Possibility
    python with as 的用法
  • 原文地址:https://www.cnblogs.com/paper-man/p/13284626.html
Copyright © 2011-2022 走看看