美文网首页
JAVA学习笔记之HashMap

JAVA学习笔记之HashMap

作者: 夏目手札 | 来源:发表于2019-05-16 15:56 被阅读0次

    目录

    1. 相关概念介绍
    2. 实现原理介绍
    3. 源码分析
    4. 总结
    5. 参考地址

    相关概念介绍

    • 数组
      采用一段连续的存储单元来存储数据。
    • 线性链表
      具有链接存储结构的线性表,它用一组地址任意的存储单元存放线性表中的数据元素,逻辑上相邻的元素在物理上不要求也相邻,不能随机存取。一般用结点描述:结点(表示数据元素) =数据域(数据元素的映象) + 指针域(指示后继元素存储位置)
    • 红黑树
      红黑树(Red Black Tree) 是一种自平衡二叉查找树,在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。相关介绍参考红黑树原理和算法
    • 哈希表
      是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
      给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
    • 哈希冲突
      如果两个不同的元素,通过哈希函数得出的实际存储地址,然后要进行插入的时候,发现已经被其他元素占用了,这就是所谓的哈希冲突,也叫哈希碰撞。

    实现原理介绍

    HashMap原理图

    简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前node的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

    源码分析

    以下所有代码基于jdk1.8

    • 成员变量
        //默认初始化容器大小 16
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
        //默认容器最大值 2^30
        static final int MAXIMUM_CAPACITY = 1 << 30;
        //默认的负载因子,当容器元素数量达到总容量*DEFAULT_LOAD_FACTOR时会进行扩容操作
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
        //默认树形阈值
        static final int TREEIFY_THRESHOLD = 8;
        //默认非树形阈值
        static final int UNTREEIFY_THRESHOLD = 6;
        //默认红黑树最小容量
        static final int MIN_TREEIFY_CAPACITY = 64;
        //数组
        transient Node<K,V>[] table;
        //使用Set存储所有的节点
        transient Set<Map.Entry<K,V>> entrySet;
        //map的大小
        transient int size;
        //hashMap修改的次数
        transient int modCount;
        //下一次扩容的阈值
        int threshold;
        //hash表的负载因子
        final float loadFactor;
    
    • 节点
    static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Node<K,V> next;
    
            Node(int hash, K key, V value, Node<K,V> next) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = next;
            }
    
            public final K getKey()        { return key; }
            public final V getValue()      { return value; }
            public final String toString() { return key + "=" + value; }
    
            public final int hashCode() {
                return Objects.hashCode(key) ^ Objects.hashCode(value);
            }
    
            public final V setValue(V newValue) {
                V oldValue = value;
                value = newValue;
                return oldValue;
            }
    
            public final boolean equals(Object o) {
                if (o == this)
                    return true;
                if (o instanceof Map.Entry) {
                    Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                    if (Objects.equals(key, e.getKey()) &&
                        Objects.equals(value, e.getValue()))
                        return true;
                }
                return false;
            }
        }
    
    • #put()操作
    public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
        
    static final int hash(Object key) {
        int h;
        //h>>>16 无符号右移16位,hash的效果等于将key的hashCode的高16位^低16位运算
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //1.如果容器还未初始化,进行resize操作
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //2.n是2的倍数所以(以n=16为例)n-1为1111,(n-1)&hash就是取hash的低四位,即保证坐标值一定是在数组范围之类
        //计算出该元素应该放入数组的下标,这里表示当该位置为null时,新增一个节点并放入
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
        //3.当不为空时,默认采用开放寻址法寻找到key相同(或者新增)的节点
            Node<K,V> e; K k;
            //3.1 如果新增的key-value已经有对应的值了,不做操作,直接返回原值
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //3.2 如果数组中的节点是树形节点,进行红黑树的插入操作
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
            //3.2 如果数组中的节点是线性链表,遍历节点,如果有相同的就break,否则将节点加入到末尾。
                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;
                }
            }
            //4. 如果e不为空,说明找到相同key,替换新的value并返回旧的value
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                //自定义扩展方法,LinkedHashMap中有实现
                afterNodeAccess(e);
                return oldValue;
            }
        }
        //5. 修改次数自增,容器大小自增,并且如果超过了阈值,进行resize操作
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    

    主要流程如下:

    • 判断HashMap是否初始化,如果还没有初始化,先初始化;
    • 通过hash&(n-1)算出在桶上的位置,如果对应位置为空,直接放入该位置中;
    • 如果桶上的对应的位置不为空,则进入对应的链表进行下一步判断:
      • 根据hash或者key来判断是否相同,相同时e=p;
      • 如果p是红黑树,则进入红黑树的插入逻辑,并返回e;
      • 遍历p链表,根据hash或者key来判断是否存在相同,如果存在直接返回e,否则创建新的节点;
    • 根据上面返回的e节点来判断,如果不为空,说明在HashMap中找到对应的节点,替换新的value值并返回旧值,结束put操作;
    • 修改容器大小,并判断是否超过阈值,如果超过进行扩容操作。
    • #resize()操作
    final Node<K,V>[] resize() {
        //1. 数据备份,数组,容量大小,扩容阈值
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        //2. 如果超过默认最大值,直接返回,否则变更大小(原大小*2)
        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; // double threshold
        }
        //3. 如果原扩容阈值>0,新的容量=原扩容阈值,否则使用默认值
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        //4. 根据负载因子计算新的扩容阈值
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        //5. 根据新的容量创建新的tab
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        //6. 进行扩容操作
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    //5.1 对于单个节点,重新计算位置并放入
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                        //5.2 树形节点单独处理
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                        //5.3 链表节点单独处理
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            //将链表的数据分成两波
                            //oldCap是旧桶的长度,是2的倍数,比如oldCap为16->10000
                            //e.hash&oldCap==0说明e.hash高位为0
                            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);
                        //(e.hash & oldCap) == 0)的即hash值高位为0的还是原来的位置
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        //(e.hash & oldCap) != 0)的即hash值高位不为0的放入oldCap+j的位置
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    

    主要流程如下:

    • 先进行数组,容量大小,扩容阈值等的备份;

    • 扩容时如果是单节点,重新计算桶的位置,新的桶位置根据hash值来,可能还在原来的位置,也可能翻倍增长,如下图中15->31;

    • 如果是红黑树节点,单独处理;

    • 如果是链表结构,将链表分为两部分,一部分hash高位为0还保持原来的位置,另一部分放到数组原来位置+oldCap的位置上。如图所示:


      链表扩容示意图
    • 与1.7版本的比较
      1.7中没有红黑树,所以代码也比较简单一点

    public V put(K key, V value) {
        //如果数组为空,扩容
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        //根据找出的索引位置去判断该位置上链表有没有相同的entry
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    
        modCount++;
        //增加entry
        addEntry(hash, key, value, i);
        return null;
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //判断是否进行扩容操作
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        //创建entry
        createEntry(hash, key, value, bucketIndex);
    }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
    
     void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
    
        Entry[] newTable = new Entry[newCapacity];
        //重点在这
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
    
     void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                //多线程下会形成闭环
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }
    

    这里的扩容多线程情况下会出现闭环现象,下面通过几张图来解释闭环的形成:
    我们假设 HashMap 从 2 resize到 4 :


    初始图

    假设我们有两个线程t1,t2,假设t1Entry<K,V> next = e.next;处挂起,t2完成了后面的操作,在按照上面的代码执行后:


    t1停止调度
    这个时候t1又恢复了调度,接着往下执行:
    t1恢复调度
    接着往下执行:
    t1执行1
    t1执行2

    闭环形成:


    闭环形成
    • #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;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //优先检查第一个节点
            if (first.hash == hash && // always check first node
                ((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;
    }
    
    • #remove()操作
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
    
    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;
        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;
            //找出需要remove的节点,跟get操作基本一致
            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);
                }
            }
            //remove对应的节点
            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;
    }
    
    • 红黑树的实现
    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;
        TreeNode(int hash, K key, V val, Node<K,V> next) {
            super(hash, key, val, next);
        }
        //返回根节点
        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }
    
        /**
         * 由于TreeNode即是树结构也是双向链表.所以这里
         * 保证树的根节点同时也是链表的首节点
         */
        static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
            int n;
            if (root != null && tab != null && (n = tab.length) > 0) {
                int index = (n - 1) & root.hash;
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
                if (root != first) {
                    Node<K,V> rn;
                    tab[index] = root;
                    TreeNode<K,V> rp = root.prev;
                    if ((rn = root.next) != null)
                        ((TreeNode<K,V>)rn).prev = rp;
                    if (rp != null)
                        rp.next = rn;
                    if (first != null)
                        first.prev = root;
                    root.next = first;
                    root.prev = null;
                }
                assert checkInvariants(root);
            }
        }
        //寻找节点
        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;
                //如果当前节点的hash大于需要寻找节点的hash,则指向其左孩子,否则指向右孩子,如果当前节点就是要寻找的节点,直接返回
                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;
        }
        //获取相应的节点
        final TreeNode<K,V> getTreeNode(int h, Object k) {
            return ((parent != null) ? root() : this).find(h, k, 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;
            //获取父节点
            TreeNode<K,V> root = (parent != null) ? root() : this;
            for (TreeNode<K,V> p = root;;) {
                int dir, ph; K pk;
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    return p;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (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);
                }
    
                TreeNode<K,V> xp = p;
                //根据hash判断,并且遍历插入到叶子节点,再进行平衡调整
                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;
                }
            }
        }
    
        //删除操作
        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;
            //1.先删除链表的关系
            if (pred == null)
                tab[index] = first = succ;
            else
                pred.next = succ;
            if (succ != null)
                succ.prev = pred;
            if (first == null)
                return;
            //2.开始删除树形关系
            if (root.parent != null)
                root = root.root();
            //如果链表太小,从红黑树转化成普通链表
            if (root == null || root.right == null ||
                (rl = root.left) == null || rl.left == null) {
                tab[index] = first.untreeify(map);  // too small
                return;
            }
            //2.1 被删除节点左孩子与右孩子都不为空
            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;
                //如果pr就是其后继,直接交换位置
                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)
                //2.2 左子树不为空
                replacement = pl;
            else if (pr != null)
                //2.3 右子树不为空
                replacement = pr;
            else
                //2.4 左右子树都为空
                replacement = p;
            //3. 左右子树不为空进行,使用非空子树代替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;
            }
            //4. 当删除节点是黑色的时候进行平衡转化
            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
            //5. 点左右子树都为空,直接删除p节点
            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);
        }
    
        //左旋(动手画一下就懂了)
        static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
            TreeNode<K,V> r, pp, rl;
            if (p != null && (r = p.right) != null) {
                if ((rl = p.right = r.left) != null)
                    rl.parent = p;
                if ((pp = r.parent = p.parent) == null)
                    (root = r).red = false;
                else if (pp.left == p)
                    pp.left = r;
                else
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
        }
        //右旋(同理,动手画一下)
        static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                               TreeNode<K,V> p) {
            TreeNode<K,V> l, pp, lr;
            if (p != null && (l = p.left) != null) {
                if ((lr = p.left = l.right) != null)
                    lr.parent = p;
                if ((pp = l.parent = p.parent) == null)
                    (root = l).red = false;
                else if (pp.right == p)
                    pp.right = l;
                else
                    pp.left = l;
                l.right = p;
                p.parent = l;
            }
            return root;
        }
        //插入平衡转化
        static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
            //默认插入的节点为红色
            x.red = true;
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                //1.x为根节点,根节点默认为黑色,并直接返回
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                else if (!xp.red || (xpp = xp.parent) == null)
                //2.父节点为黑色,或者祖父节点为空,即父节点是根节点,此时不需要调整
                    return root;
                //3.分类讨论,xp为左节点或者右节点
                if (xp == (xppl = xpp.left)) {
                    //3.1. 再次分类,如果x的叔父节点:xppr,不为空且为红色节点,此时先进行部分颜色调整
                    if ((xppr = xpp.right) != null && xppr.red) {
                        //父节点,叔父节点变为黑色,祖父变为红色,x变成祖父节点
                        xppr.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        //3.1.1. 再次分类,如果x为xp的右孩子,则对xp进行左旋
                        if (x == xp.right) {
                            root = rotateLeft(root, x = xp);
                            //重新对xp,xpp定义
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        //这里xp为原来的x为红色,x也是红色,所以先进行颜色调整,然后进行右旋
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                    //镜像操作
                    if (xppl != null && xppl.red) {
                        xppl.red = false;
                        xp.red = false;
                        xpp.red = true;
                        x = xpp;
                    }
                    else {
                        if (x == xp.left) {
                            root = rotateRight(root, x = xp);
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            xp.red = false;
                            if (xpp != null) {
                                xpp.red = true;
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }
        //删除平衡转化
        static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                                   TreeNode<K,V> x) {
            for (TreeNode<K,V> xp, xpl, xpr;;)  {
                if (x == null || x == root)
                    return root;
                else if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                }
                else if (x.red) {
                    //1.如果x的红色节点,修改为黑色,无需调整结构,直接返回
                    x.red = false;
                    return root;
                }
                else if ((xpl = xp.left) == x) {
                    //2.x为左节点
                    if ((xpr = xp.right) != null && xpr.red) {
                    //如果x的叔父节点为红色,此时左边比右边矮,需要左旋
                        xpr.red = false;
                        xp.red = true;
                        root = rotateLeft(root, xp);
                        xpr = (xp = x.parent) == null ? null : xp.right;
                    }
                    //如果xpr为空,x指向xp
                    if (xpr == null)
                        x = xp;
                    else {
                    //如果xpr不为空,则分别对xpr的左右孩子进行分类
                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                        if ((sr == null || !sr.red) &&
                            (sl == null || !sl.red)) {
                            //如果左孩子,右孩子满足为空或者为黑色节点,xpr转为红色,x指向xp,进入下一次循环,xp会被转成黑色,满足红黑树条件
                            xpr.red = true;
                            x = xp;
                        }
                        else {
                            if (sr == null || !sr.red) {
                            //xpr左子树不为空且为红色
                                if (sl != null)
                                //xpr左子树不为空,将其变成黑色
                                    sl.red = false;                                                                                                             //xpr变成红色,不满足红黑树3,4,需要进行右旋
                                xpr.red = true;
                                root = rotateRight(root, xpr);
                                xpr = (xp = x.parent) == null ?
                                    null : xp.right;
                            }
                            if (xpr != null) {
                                xpr.red = (xp == null) ? false : xp.red;
                                if ((sr = xpr.right) != null)
                                    sr.red = false;
                            }
                            if (xp != null) {
                                xp.red = false;
                                root = rotateLeft(root, xp);
                            }
                            x = root;
                        }
                    }
                }
                else { // symmetric
                    if (xpl != null && xpl.red) {
                        xpl.red = false;
                        xp.red = true;
                        root = rotateRight(root, xp);
                        xpl = (xp = x.parent) == null ? null : xp.left;
                    }
                    if (xpl == null)
                        x = xp;
                    else {
                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
                        if ((sl == null || !sl.red) &&
                            (sr == null || !sr.red)) {
                            xpl.red = true;
                            x = xp;
                        }
                        else {
                            if (sl == null || !sl.red) {
                                if (sr != null)
                                    sr.red = false;
                                xpl.red = true;
                                root = rotateLeft(root, xpl);
                                xpl = (xp = x.parent) == null ?
                                    null : xp.left;
                            }
                            if (xpl != null) {
                                xpl.red = (xp == null) ? false : xp.red;
                                if ((sl = xpl.left) != null)
                                    sl.red = false;
                            }
                            if (xp != null) {
                                xp.red = false;
                                root = rotateRight(root, xp);
                            }
                            x = root;
                        }
                    }
                }
            }
        }
    }
    

    总结

    参考地址

    红黑树原理和算法
    红黑树插入图解
    二叉树的遍历规则
    红黑树化过程
    推荐:并发情况下:Java HashMap 形成死循环的原因

    相关文章

      网友评论

          本文标题:JAVA学习笔记之HashMap

          本文链接:https://www.haomeiwen.com/subject/kzwyaqtx.html