HashMap源码阅读——红黑树

标签: 源码 hashmap 红黑树
18人阅读 评论(0) 收藏 举报
分类:

HashMap源码阅读——红黑树

上节我们提到了jdk1.8中引入了红黑树来解决一个桶下链表过长的问题。

关键参数

HashMap中有三个关于红黑树的关键参数

//一个桶的树化阈值
//当桶中元素个数超过这个值时,需要使用红黑树节点替换链表节点
//这个值必须为 8,要不然频繁转换效率也不高
static final int TREEIFY_THRESHOLD = 8;
//一个树的链表还原阈值
//当扩容时,桶中元素个数小于这个值,就会把树形的桶元素 还原(切分)为链表结构
//这个值应该比上面那个小,至少为 6,避免频繁转换
static final int UNTREEIFY_THRESHOLD = 6;
//哈希表的最小树形化容量
//当哈希表中的容量大于这个值时,表中的桶才能进行树形化
//否则桶内元素太多时会扩容,而不是树形化
//为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
static final int MIN_TREEIFY_CAPACITY = 64;

红黑树定义

  • 红黑树的每个节点的颜色非红即黑
  • 根节点是黑色的
  • 空叶子节点是黑色的
  • 如果一个节点是红色的,那么它的子节点必须是黑色的
  • 从任意节点都该节点的子孙节点的所有路径上包含相同数目的黑点树
 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;
}

红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)

treeifyBin

HashMap中树形化最重要的一个方法treeifyBin() 即树形化。在一个桶中的元素个数超过 TREEIFY_THRESHOLD(默认是8),就使用红黑树来替换链表。

final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        //如果hash表为空或者hash表的容量小于MIN_TREEIFY_CAPACITY(64),那么就去新建或者扩容
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                //新建一个树形节点,内容和当前链表节点一致
                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);
        }
}
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
        return new TreeNode<>(p.hash, p.key, p.value, next);
    }

红黑树的具体格式化过程

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)//这里的hash是经过扰动函数之后得到的hash
                            dir = -1;
                        else if (ph < h)
                            dir = 1;
                        else if ((kc == null &&     //hash一样且(key值不可比较或者两者key值相等)
                                  (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); //把根节点放到桶中
}

可以看到,将二叉树变为红黑树时,需要保证有序。这里有个双重循环,拿树中的所有节点和当前节点的哈希值进行对比(如果哈希值相等,就对比键,这里不用完全有序,只是保证rebalance时的插入一致性)

参考文献

Java 集合深入理解(17):HashMap 在 JDK 1.8 后新增的红黑树结构
重温数据结构:深入理解红黑树
红黑树代码实现

查看评论

Access超级经典源码剖析--列表框的高级应用

-
  • 1970年01月01日 08:00

JAVA8 hashmap源码阅读笔记(红黑树链表)

一:hashmap的13 个成员变量 static final int DEFAULT_INITIAL_CAPACITY = 1 数组默认初始容量:16 static final int MAXIM...
  • crpxnmmafq
  • crpxnmmafq
  • 2017-07-18 22:08:29
  • 5381

HashMap 在 JDK 1.8 后新增的红黑树结构

读完本文你将了解到: 点击查看 Java 集合框架深入理解 系列 - - 乾杯传统 HashMap 的缺点HashMap 在 JDK 18 中新增的数据结构 红黑树Has...
  • wushiwude
  • wushiwude
  • 2017-07-18 23:23:17
  • 2841

ConcurrentHashMap的红黑树实现分析

知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得 红黑树 红黑树是一种特殊的二叉树,主要用它存储有序的数据,提供高效的数据检索,时间复杂度为O(lgn),每个节点都有一个标识位...
  • wfg18801733667
  • wfg18801733667
  • 2017-02-23 09:05:24
  • 1590

Java 集合深入理解(17):HashMap 在 JDK 1.8 后新增的红黑树结构

点击查看 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~ 上篇文章我们介绍了 HashMap 的主要特点和关键方法源码解读,这篇文章我们介绍 HashMap 在 JDK1.8...
  • u011240877
  • u011240877
  • 2016-11-26 23:24:32
  • 13837

jdk1.8对于HashMap碰撞处理的优化-引入红黑树

jdk1.8对于HashMap碰撞处理的优化-引入红黑树                      在2014年,jdk1.8发布。Java8的HashMap对之前做了较大的优化,其中最重要的一个...
  • mengyue000
  • mengyue000
  • 2017-08-23 11:52:09
  • 751

HashMap中红黑树操作实现

// 红黑树操作方法实现, 从CLR引入 static TreeNode rotateLeft(TreeNode root, ...
  • ymrfzr
  • ymrfzr
  • 2016-04-25 17:42:25
  • 1385

java心得(hashmap之红黑树)

因为在JDK1.8后在hashmap基础上增加了红黑树,所以百度学习了解下红黑树 1.为什么要增加红黑树? 因为之前hashmap底层结构是数组加链表,但是当数据大到一定程度的时候,即使是用链表存储也...
  • weixin_40174677
  • weixin_40174677
  • 2017-12-10 20:23:46
  • 109

HashMap源码分析(四)put-jdk8-红黑树的引入

HashMap jdk8以后他的逻辑结构发生了一点变化: 大概就是这个意思: 当某一个点上的元素数量打到一定的阈值的时候,链表会变成一颗树,这样在极端情况下(所有的元素都在一个点上,整个...
  • q291611265
  • q291611265
  • 2015-07-08 00:20:01
  • 4628

【死磕Java并发】-----J.U.C之ConcurrentHashMap红黑树转换分析

原文出处http://cmsblogs.com/ 『chenssy』 在【死磕Java并发】—–J.U.C之Java并发容器:ConcurrentHashMap一文中详细阐述了ConcurrentHa...
  • chenssy
  • chenssy
  • 2017-06-26 22:32:25
  • 7075
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 500
    排名: 10万+
    文章存档