前言

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. frp 路由穿透(github开源穿透软件)

    server配置(windows):下载: https://github.com/fatedier/frp/releases [common] # 服务器端端口 bind_port = # 客户端连接 ...

  2. Windows地址栏的妙用

    主角: 它就是windows自带的一个小工具->地址栏,可以通过在任务栏右键选择工具栏-地址栏添加使用. 妙用: 一.打开文件 使用方法:D:\Temp(文件路径) 小提示:快速进入回收站:Re ...

  3. C#字符串和16进制字符串之间的转换

    将字符串编码成 16进制 字符串表示: using System;using System.Collections.Generic;using System.Linq;using System.Tex ...

  4. oracle下关于table的常用sql整理

    创建表,create TABLE table( 列名称1 数据类型1, 列名称2 数据类型2, 列名称3 数据类型3, ......); eg: create table TABLE_24751( i ...

  5. django_celery_results安装的坑

    前言  在Celery4.0之前的版本中,有一个专门供Django使用的Celery版本django-celery.但现在Celery已经统一为一个版本,所以直接安装原生的Celery即可.这里就暂时 ...

  6. python基础编程: 函数示例、装饰器、模块、内置函数

    目录: 函数示例 装饰器 模块 内置函数 一.函数示例: 1.为什么使用函数之模块化程序设计: 不使用模块程序设计的缺点: 1.体系结构不清晰,可主读性差: 2.可扩展性差: 3.程序冗长: 2.定义 ...

  7. idea 下gradle创建springboot 项目

    InterlijIdea 开发环境下创建基于springBoot的项目. 环境 1.jdk1.5以上 2.interlijidea 15 以上 步骤 1.File –>new –>Proj ...

  8. 内核模式构造-Mutex构造(RecursiveAutoResetEvent)

    internal sealed class RecursiveAutoResetEvent : IDisposable { private AutoResetEvent m_lock = new Au ...

  9. npm run build后如何打开index.html跑起项目

    Tip: built files are meant to be served over an HTTP server.   Opening index.html over file:// won't ...

  10. (转)ORACLE中关于外键缺少索引的探讨和总结

    在ORACLE数据库中,定义外键约束时,ORACLE是不会自动创建对应索引的,必须手动在外键约束相关的列上创建索引.那么外键字段上是否有必要创建索引呢?如果有必要的话,巡检时,如何找出外键字段上没有创 ...