HashMap实现原理(jdk1.7/jdk1.8)
HashMap的底层实现:
1、简单回答
JDK1.7:HashMap的底层实现是:数组+链表
JDK1.8:HashMap的底层实现是:数组+链表/红黑树
为什么要红黑树?
红黑树:一个自平衡的二叉树
当结点多了用红黑树,少了用链表
因为少的话用红黑树太复杂,多了话用红黑树可以提高查询效率。
红黑树:(自动调整根结点,保证左右两边的结点数差不多),它会左旋,右旋来实现。
JDK1.7:HashMap的底层实现是:数组+链表
2、复杂回答v1.0版
每一个对映射关系的存储位置:
存储的位置:
(1)先计算key的hash值,通过hashCode()就可以得到,
(2)再用hash值经过各种(异或)操作,得到一个int数字,目的是使得更均匀的分布在数组的每一个元素中。
(3)再用刚才的int值,与table数组的长度-1做一个“按位与&"
运算,目的是得到一个[i]
因为数组的长度是2的n次方,长度-1的数的二进制是前面都是0,后面都是1,任何数与它做“&”,结果一定是[0,该数]之间
为什么会出现链表?
(1)两个不同的key对象,仍然可能出现hashCode值一样
(2)就算hashCode不一样,经过刚才的计算,得到[i]是一样的
(3)而我们的数组的元素table[i]本来只能放一个元素,那么现在有多个元素需要存储到table[i]中,只能把这几个元素用链表连接起来
简单比喻:
y = f(x)
两个不一样的x,可能得到一样的y
那么存储到HashMap中的是什么样的元素呢?
(1)首先是Map.Entry类型:映射项(键-值对)。
(2)其次HashMap有一个内部类HashMap.Entry实现了Map.Entry接口类型
内部类Entry由四部分组成:
(1)key
(2)value
(3)下一个Entry:next
(4)hash值计算的整数值,为了后面查询快一点
如何避免重复?
如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回true,就会把新的value替换旧的value。
如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回false,就会把新的Entry连接到旧的Entry所在链表的头部(first)
如果两个key的hash值是不一样的,但是[i]是一样的,就会把新的Entry连接到旧的Entry所在链表的头部(first)
如果两个key的hash值是不一样的,并且[i]不一样的,肯定存在不同table[i]中
我们把table[i]称为“桶bucket”。
回忆:两个对象的hash值:
(1)如果两个对象是“相等”,他们的hash值一定是一样
(2)如果两个对象的hash值是一样,他们可能是相同的对象,也可能是不同的对象。
3、复杂追踪源代码v2.0版
(1)什么时候扩容
当元素的个数达到“阈值”,并且新添加的映射关系计算出来的table[i]位置是非空的情况下,再扩容
默认加载因子 (0.75):DEFAULT_LOAD_FACTOR
阈值 = table.length * 加载因子(loadFactor)
第一次阈值:16 * 0.75 = 12
初始容量:DEFAULT_INITIAL_CAPACITY:16
第二次阈值:32 * 0.75 = 24
...
HashMap中table的长度有一个要求:必须是2的n次方
(2)跟踪一下put方法的源代码
第一步:如果table是空的,会先把table初始化为一个长度为16的数组,如果你指定的长度不是2的n次方,会往上纠正为最接近的2的n次方
并且把阈值 threshold= table.length * 加载因子(loadFactor) = 12。
第二步:如果key是null,首先确定的位置是table[0],
如果原来table[0]已经有key为null的Entry,用新的value替换旧的value
如果原来table[0]没有key为null的Entry,那么创建一个新的Entry对象,作为table[0]桶下面的链表的头,原来的那些作为它next。
第三步:如果key不是null,那么用key的hashCode值,通过hash()函数算出一个int值称为"hash"
第四步:通过刚才的“hash”的int值与table.length-1做&运算,得到一个下标index,表示新的映射关系即将存入table[index]
第五步:循环判断table[index]位置是否为空,并且是否有Entry的key与我新添加的key是否“相同”,如果相同,就用新的value替换旧的value
第六步:添加新的映射关系
(1)判断是否要扩容:
当元素的个数达到“阈值”,并且新添加的映射关系计算出来的table[i]位置是非空的情况下,table再扩容为原来的2倍长
如果扩容了,那么要重新计算hash和index
(2)把新的映射关系创建为一个Entry的对象放到table[index]的头部。
JDK1.8:HashMap的底层实现是:数组+链表/红黑树
1、复杂回答v1.0
(1)映射关系的类型
添加到HashMap1.8种的映射关系的对象是HashMap.Node类型,或者是HashMap.TreeNode类型。
它也是Map.Entry接口的实现类。
(2)映射关系添加到table[i]中时,如果里面是链表,新的映射关系是作为原来链表的尾部
“七上八下”:JDK1.7在上,JDK1.8在下。
为什么要在下面,因为如果是红黑树,那么是在叶子上,保持一致,都在下面。
(3)扩容的时机不同
第一种扩容:元素个数size达到阈值threshod = table.length * 加载因子 并且table[i]是非空的
第二种扩容:当某个桶table[index]下面的结点的总数原来已经达到了8个,这个时候,要添加第9个时,会检查
table.length是否达到64,如果没达到就扩容。如果添加第10个时,也会检查table.length是否达到64,如果没达到就扩容。
为什么,因为一旦扩容,所有的映射关系要重新计算hash和index,一计算原来table[index]下面可能就没有8个,或新的映射关系也可能不在table[index],
这样就可能均匀分布。
(4)什么时候从链表变成红黑树
当table[index]下面结点的总数已经达到8个,并且table.length也已经达到64,那么再把映射关系添加到
table[index]下时,就会把原来的链表修改红黑树
(5)什么时候会从红黑树变为链表
当删除映射关系时table[index]下面结点的总数少于6个,会把table[index]下面的红黑树变回链表。
2、put的源代码v2.0
第一步:计算key的hash,用了一个hash()函数,目的得到一个比hashCode更合理分布的一个int值
第二步:如果table是空的,那么把table初始化为长度为16的数组,阈(yu)值初始化为= 默认的长度16 * 默认的加载因子0.75 = 12
DEFAULT_INITIAL_CAPACITY:默认的初始化容量16
DEFAULT_LOAD_FACTOR:默认加载因子 0.75
第三步:查看table[index]是否为空,如果为空,就直接new一个Node放进去
index = hash的int值 & table.length-1
第四步:先查看table[index]的根节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
第五步:如果table[index]的根节点的key与新添加的映射关系的key不同,
还要看table[index]根结点的类型是Node还是TreeNode类型,
如果是Node类型,那么就查看链表下的所有节点是否有key与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
如果是TreeNode类型,那么就查看红黑树下的所有叶子节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value。
第六步:如果没有找到table[index]有结点与新添加的映射关系的key“相同”,那么
如果是TreeNode类型,那么新的映射关系就创建为一个TreeNode,连接到红黑树中
如果是Node类型,那么要查看链表的结点的个数,是否达到8个,如果8个,并且table.length小于64,那么先扩容。
TREEIFY_THRESHOLD:树化阈值8
MIN_TREEIFY_CAPACITY:最小树化容量64
UNTREEIFY_THRESHOLD:反树化,从数变为链表的阈值6
HashMap实现原理(jdk1.7/jdk1.8)的更多相关文章
- Jdk1.8中的HashMap实现原理
HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. HashM ...
- 【1】Jdk1.8中的HashMap实现原理
HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 内部实现 ...
- JDK1.8的HashMap实现原理和源码解析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构.许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中的对应实现HashMap的 ...
- HashMap实现原理(jdk1.7),源码分析
HashMap实现原理(jdk1.7),源码分析 HashMap是一个用来存储Key-Value键值对的集合,每一个键值对都是一个Entry对象,这些Entry被以某种方式分散在一个数组中,这个数 ...
- HashMap实现原理及源码分析(JDK1.7)
转载:https://www.cnblogs.com/chengxiao/p/6059914.html 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
- hashmap源码解析,JDK1.8和1.7的区别
背景:hashmap面试基础必考内容,需要深入了解,并学习其中的相关原理.此处还要明白1.7和1.8不通版本的优化点. Java 8系列之重新认识HashMap Java 8系列之重新认识HashMa ...
- HashMap实现详解 基于JDK1.8
HashMap实现详解 基于JDK1.8 1.数据结构 散列表:是一种根据关键码值(Key value)而直接进行访问的数据结构.采用链地址法处理冲突. HashMap采用Node<K,V> ...
- HashMap 源码详细分析(JDK1.8)
一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...
- HashMap实现原理和源码解析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构.许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中的对应实现HashMap的 ...
随机推荐
- Python自定义排序
比如自定义了一个class,并且实例化了这个类的很多个实例,并且组成一个数组.这个数组要排序,是通过这个class的某个字段来排序的.怎么排序呢? 有两种做法: 第一种是定义__cmp__( )方法: ...
- Java链表和递归
删除链表的指定元素: public class ListNode { public int val; public ListNode next; public ListNode(int x){ val ...
- 在 Windows服务器中启用/禁用SMBv1、SMBv2和SMBv3的方法
本文介绍如何在 SMB 客户端和服务器组件上启用/禁用服务器消息块 SMBv1.SMBv2 和 SMBv3. 注意:建议由专业技术工程师完成以下操作. 禁用 SMBv2 和 SMBv3 的影响 我们建 ...
- 如何设置Navicat的显示字体与字体大小?
方法/步骤 打开Navicat 点击[工具]菜单,再选择[选项] 在[选项]界面,点击[外观]下的[字体] 设置网格字体和大小 设置编辑器字体和大小 设置命令列界面字体和大 ...
- Linux下Nginx安装/启动/重启/停止
Nginx是高性能的web服务器也是非常好用反向代理服务器,可以实现负载均衡,动静分离等策略,在linux下用的非常多.下面是下载地址 http://nginx.org/en/download.h ...
- SQL Server 常用函数使用方法
之前就想要把一些 SQL 的常用函数记录下来, 直到今天用到substring()这个函数,C# 里面这个方法起始值是 0,而 SQL 里面起始值是 1.傻傻分不清楚... 这篇博客作为记录 SQL ...
- java.net.UnknownHostException: master
1:如果你报这个错误,第一反应应该是本地的host文件没有配置服务器名称和对应的ip地址,这个反应就对了.贴一下错误和解决方法: java.net.UnknownHostException: mast ...
- scrollReveal.js – 页面滚动显示动画JS
简介 和 WOW.js 一样,scrollReveal.js 也是一款页面滚动显示动画的 JavaScript ,能让页面更加有趣,更吸引用户眼球.不同的是 WOW.js 的动画只播放一次,而 ...
- Web.Config引入配置ConfigSource
1.配置文件要和Config文件通一个项目 2.注意路径的写法 3.appSettings和connectionStrings等都可以设置configSource 4.这样发布到不同的环境的时候,改动 ...
- Atcoder ARC101 Ribbons on Tree
题解: 前面牛客网的那个比赛也有一道容斥+dp 两道感觉都挺不错的 比较容易想到的是 f[i][j]表示枚举到了i点,子树中有j个未匹配 这样的话我们需要枚举儿子中匹配状态 这样是n^2的(这是个经典 ...