前言

JDK8中对HashMap做了优化,依然是用数组存储数据,但是扩容时采用双链表的方式避免了高并发情况下导致出现循环链表的问题,另外也引入了红黑树,提高碰撞元素的搜索速度。

一段代码

下面这段代码创建一个容量为64的HashMap和插入一些数据。

HashMap<Integer,Integer> hashMap=new HashMap<>(64);
hashMap.put(5,5);
hashMap.put(2,2);
hashMap.put(69,69);
hashMap.put(66,66);
hashMap.put(325,325);
hashMap.put(197,197);
hashMap.put(261,261);
hashMap.put(133,133);
hashMap.put(389,389);
hashMap.put(453,453);
hashMap.put(133,999);

如何定位数据下标

一开始说到HashMap是用数组来存储数据,那么数组下标和key是怎么关联上的呢。其实它是将key的hash值和数组长度进行一系列的位运算(异或和与)得出数据下标。例如key=69时,运算如下

最后得出下标是5,那就把(69,69)包装成Node<Integer,Integer>放在数组5的位置上。

链表

从上面公式可以看到,大小相差64的key计算出来的位置都是一样的,例如5和69计算出来的下标都是5,产生冲突了。数组一个下标只能存储一个元素,那么怎么办。HashMap这个时候会用链表来存储冲突的元素,第一个存储在数组的就是链表头Node,接下来冲突元素放在链表头Node的Next位置上,上面那段代码执行流程如下:

从上面的动图可以看到,每个元素都是链表顺序往下找,如果找到相同key的元素,就替换value,否者就放在链表的尾元素的next上。那么问题来了,如果链表很长,每次都要一直遍历到尾部,这太耗时了,时间复杂度为O(n)。如果将链表改成红黑树,那么时间复杂度将会是O(logn)。事实上,HashMap就是这么干的。

红黑树

在HashMap中,当链表长度大于8时,链表将会转成红黑树。而红黑树的根会存储在数组中。如果这个时候再put一个元素(517,517),将会触发树转换,最终转换的结果如下

在说转换之前,可以先来了解一下红黑树的概念。其实红黑树是近似二叉平衡树的,二叉平衡树又是二叉树的一种,他们之间的关系如下

我个人觉得从二叉搜索树开始,在计算机科学中才有一定价值。每一种树都有性质,性质是继承的,越里面的树性质越多。

  • 二叉搜索树:它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值。
  • 平衡二叉搜索树:在二叉树的基础上,加上性质:它的左右两个子树的高度差的绝对值不超过1。平衡二叉搜索树搜索复杂度总是O(logn),有效规避了有序元素插入的情况导致退化成链表的问题。
  • 红黑树:在二叉搜索树的基础上,加入四个性质,使树总是保持近似平衡,搜索时间复杂度也是O(logn)。

1. 节点是红色或黑色。

2. 根节点是黑色。

             3 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
             4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
      红黑树是通过一定算法来满足上面四个性质,以达到一种近似平衡的状态。与其说算法,不如说公式吧。玩过魔方的都知道,要还原只需要根据几个公式不断地旋转,到最后就不知不觉还原了。其实红黑树也差不多,每插入一个新的元素都可能破坏红黑树的性质,这个时候红黑树会根据几个公式来还原。它们分别为变色,左旋和右旋。通常情况下,几个公式都是组合使用的。
      现在回到刚才转换的话题。首先HashMap会将链表转成树节点的链表,然后根据链表顺序,一个个元素插入到红黑树中,每插入一个元素,都做一次树平衡的调整,理论上最多调整3次,调整流程如下:

按照红黑树的性质,每次新插入的都是在树的最下面,为红叶子,因为红叶子可能都不用调整,黑叶子100%要调整。从上面的流程图可以看到,调整都是从新增的红叶子开始的,通过变色或者左旋右旋的操作不断向上调整,直到调整到根节点。

HashMap三两事的更多相关文章

  1. [转]关于AS3 Socket和TCP不得不说的三两事

    磨刀不误砍柴工,让我们从概念入手,逐步深入. 所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络 ...

  2. Spring配置之标签的三两事

    今天看到项目的配置,看后就当是复习了.懵懵懂懂工作两年,感觉虽然自己一直在使用spring作为工具,但是也没有涉及到spring的方方面面,今天的研究从一次无意的自动补全开始,还是那句话,不爱研究的程 ...

  3. vue打包之部署在非根路径下的三两事

    首先,感叹一下,2019年已经过去一半,想想自己做了些什么,好像也没做什么. 今天试着配一个nginx,以前的nginx都是指向的/根路径,今天的nginx指向的非/根路径,遇到许多问题的,总结总结. ...

  4. C++:MEMSET的大坑三两事

    之前写了一题费用流,竟然硬是在写SPFA时为DIS数组赋初始值用了MEMSET数组QAQ 调试了很久也没有弄明白自己是卡在那里了,,,感觉被自己蠢哭了QWQ 错误的姿势!! #include < ...

  5. nodejs之mock与跨域代理的三两事

    emmm...好久没写博客了,都忘了该怎么开始. 那就先说下mockjs.因为一些原因,导致后台接口没有数据,那么我们就开始自己造数据,使用的是比较流行mockjs,根据文档就能简单的配置,然后开始愉 ...

  6. iad 集成三两事

    1. 好像是随着ios8的beta开始.  iad 已经发生了一些变化. 比如找不到enable iad network 的按钮了. 貌似是不需要手动去 enable 了. 只需要嵌入 iad fra ...

  7. 微信小程序:开发之前要知道的三件事

    前言 微信之父张小龙在年初的那次演讲中曾表示:"我自己是很多年的程序员,我觉得我们应该为开发的团队做一些事情".几个月后,微信正式推出微信应用号(即微信小程序),在互联网中掀起了又 ...

  8. <转>离婚前夜悟出的三件事

    文/铁眼(简书作者)原文链接:http://www.jianshu.com/p/832be4f659a0?utm_campaign=hugo&utm_medium=reader_share&a ...

  9. day16模块,导入模板完成的三件事,起别名,模块的分类,模块的加载顺序,环境变量,from...import语法导入,from...import *,链式导入,循环导入

    复习 ''' 1.生成器中的send方法 -- 给当前停止的yield发生信息 -- 内部调用__next__()取到下一个yield的返回值 2.递归:函数的(直接,间接)自调用 -- 回溯 与 递 ...

随机推荐

  1. ElementUI Hello World

    Vue Element UI Hello World <!DOCTYPE html> <html> <head> <meta charset="ut ...

  2. Odoo的 数据添加修改删除代码和对应的方式

    完整的可用命令如下: (0, _ , {‘field’: value})新建一条记录并将其与之关联 (1, id, {‘field’: value})更新已关联记录的值 (2, id, _)移除关联并 ...

  3. 0、CentOS7系统root密码丢失找回方法

    第一步 1.重新启动或开启CentOS7.6系统,在选择进入系统的Grub菜单界面如下图1-1,根据提示按"e"小写字母进入编辑界面,如下图1-2所示: 图1-1 CentOS7. ...

  4. 第四章· MySQL客户端工具及SQL讲解

    一.客户端命令介绍 1.mysql 1.用于数据库的连接管理 1) 连接(略) 2) 管理: #MySQL接口自带的命令 \h 或 help 或? 查看帮助 \G 格式化查看数据(key:value) ...

  5. Activity的跳转及返回值 的四种方法

    Activity生命周期 从创建到销毁的生命周期: onCreate()→onStart()→onResume()→onPouse()→onStop()→onDestroy() 从起动到后台再到前台: ...

  6. 使用tomcat-maven-plugin自动化部署应用

    目标:将应用自动打包并自动部署到远程tomcat容器. 1.在pom.xml中添加plugin <plugin> <groupId>org.apache.tomcat.mave ...

  7. unzip解压3G或者4G以上文件失败的解决方法

    Linux下,使用unzip解压时,报错:End-of-central-directory signature not found.  Either this file is nota zipfile ...

  8. JS 函数相关的声明调用

    // 函数声明方法一 function f (a, b) { return a + b; } // 函数调用 console.log(f(1, 4)); // 函数声明方法二 var num = fu ...

  9. tensorflow2.0编程规范

    背景 tensorflow2.0 相比于1.0 有很大变化,1.0版本的placeholder,Session都没有了,2.0版本强推使用keras.keras是一个比较高层的api,确实挺好用的,一 ...

  10. HDU - 6435 Problem J. CSGO (曼哈顿距离变换)

    题目大意:有两类武器(主武器和副武器),每类有若干把,每把武器都有一个基础属性S,以及k个附加属性,让你选一把主武器M和一把副武器S,使得最大. 显然后面的和式是一个k维的曼哈顿距离,带绝对值符号不好 ...