初学者-通过Java的HashMap了解红黑树(一)

129 阅读4分钟

什么是红黑树?为什么Java的HashMap结构里要引入红黑树?

  在jdk1.7到jdk1.8的更新中,HashMap引入了红黑树的数据结构,为啥呢,有什么好处呢,这就要把jdk1.7和jdk1.8的HashMap的结构拿来对比一番。

jdk1.7中的HashMap数据结构

  在jdk1.7中,HashMap是使用一个Entry(一个实体类)来存储数据的,但是这个Entry是数组加链表结构,啥?数组还能和链表一起玩?请看图:

image.png   这个图是一个数组,描述的是一个添加元素的过程,大家可以按照存入第n个数的顺序来看。存一入一个元素时,会附带地存入一个hashcode值和下一项的地址,这个先不深究,先往下看。
  细心的你发现了,这个数组的长度只有8(HashMap定义的数组长度不一定为8)。那HashMap可以一直add()添加元素,这数组长度只有8,能存那么多东西吗?   等等,添加第四个值的时候,好像有些不对劲,它跟在了第二个数的后面。这里其实就是链表了,可以看到,第二个数的下一项地址指向了第四个数,这里就可以解释,为什么长度为8的数组可以存无穷尽个元素。好了,新问题来了,第四个数为什么跟在第二个数后面,而不是其他数后面?先来了解一个小知识。

哈希计算   HashMap在添加数据时,会有一个哈希计算。计算过程如下:
  添加的第一个元素哈希值为2300(Java创建对象时赋予的)

  2300(哈希值)➗8(数组长度)=287(这个不重要)---------4(余数

  得到余数为4,那么这个对象就会存在Entry[4]这个位置上。第二元素的hashcode为1354,除以8后,余数为2,放在Entry[2]的位置。哦~原来HashMap在添加元素时候,不是乱添加的。下一个问题也随之而出,如果取余数结果相同,要怎么添加? 看图:

实体类代码如下

Node{
    Node Parent;
    Node Son;
    Object object;
    Node(Object object){//这个一般是泛型做到传啥是啥,不多说,懂得都懂
        this.object = object;//上一条注释不负责,不懂的可以去看看Java泛型
    }
}

现在很清晰了
存第四个对象只要两行代码就搞定:

//node是第二个数,将第四个数存在第二个数后面的过程
node.Son = new Node(Object);//创建个新节点,赋给Entry[2]的子节点
node.Son.Parent = node;//父子节点互相指向

  现在对jdk1.7的HashMap应该有了一定了解,有没有想到有什么不妥,或者还可以优化的地方?
如果一堆对象,他们的哈希值除以8,余数全是2呢
  那数组的某一个位置岂不是拖着很长的链表,并且想要查询某一个元素时候,要一个一个往后查,查询的速度就慢了。红黑树就能解决这个问题。

jdk1.8中的HashMap数据结构

  通过上一小节,jdk1.7的HashMap由数组+链表组成。那么,jdk1.8的HashMap呢,是由数组+链表+红黑树组成。
  我们先从数组加链表的结构谈到红黑树。首先链表和树的区别,一条直链在查找元素时,只能一条一条遍历。而对于树来说,可以通过特殊的遍历方法,如先序、中序、后序等遍历方法来加快查找速度。其中,二叉树的表现更优。红黑树就是二叉树的一种。先来看个图。

VBH$PO}B_W2`_KG5UOBJ7_K.png 提问:这两棵树谁先能查找到H?
查找方法为,H比A小,查询A的左节点,比B小,查询B的左节点...
我们来复习一下数据结构知识

Node{
    Node Parent;
    Node Left;
    Node Right;
    Object object;
    Node(Object object){
        this.object = object;
    }
}

查询代码如下:

public Node find(Node node, hashCode){//该方法传入
    //根节点和要查数据的哈希值即可开始遍历查找
    if(hashCode == node.hashCode){
        return node;
    }
    else if(hashCode < node.hashCode){
        return find(node.Left);//当被查找的哈希值小于当前节点的哈希值
                               //再调用自己,并传入左节点
                               //我调我自己----递归
    }else{
         return find(node.Right);
    }
}

那么得到的路径为:
  第一棵树的查找路径:A->B->D->F->H
  第二棵树的查找路径:A->F->H
  显然第二棵树更快一些。这是因为右边的二叉树比较平衡,就是左边和右边的节点差不多多
  红黑树就是一颗,你插入值,它能根据某种规则,更改树各个节点的位置,来使得这棵树,更加平衡,这样找某些数的时候就递归的次数就会减少。

红黑树到底是啥,我们下一篇文章见