JDK 7 中,HashMap 由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。 在 JDK 8 中,HashMap 由“数组+链表+红黑树”组成。 再哈希法:双重散列,多重散列,提供多个不同的hash函数,当R1=H1(key1)发生冲突时,再计算R2=H2(key1),直到没有冲突为止。这样做虽然不易产生堆集,但增加了计算的时间。 09、HashMap数组的长度为什么是 2 的幂次方? 2 的 N 次幂有助于减少碰撞的几率。 HashMap 构造函数允许用户传入的容量不是 2 的 n 次方,因为它可以自动地将传入的容量转换为 2 的 n 次方。 (20) = 32(2 的 5 次幂),也就是说 table 数组的长度总是 2 的次幂。
HashMap 的源码很多也很复杂,本文只是摘取简单常用的部分代码进行分析。能力有限,欢迎指正。 )) ^ (h >>> 16); } 取模,计算出下标 在计算下标的时候,让列表长度对哈希值做取模操作,让计算出来的哈希值在列表范围内,n 为list长度 i = (n - 1) & hash 为什么HashMap 的数组长度要取2的整次幂 因为这样(数组长度 - 1)正好相当于一个“低位掩码”。 以初始长度16为例,16-1=15,2进制表示是0000 1111。 参考 Java 8系列之重新认识HashMap JDK 源码中 HashMap 的 hash 方法原理是什么?胖君的回答 HashMap 源码详细分析(JDK1.8)
前文「JDK源码分析-HashMap(1)」分析了 HashMap 的内部结构和主要方法的实现原理。但是,面试中通常还会问到很多其他的问题,本文简要分析下常见的一些问题。 这里再贴一下 HashMap 内部的结构图(JDK 1.8): ? FAQ: Q1: HashMap 是否线程安全?为什么? 首先 HashMap 是线程不安全的。 case 2: 线程 T1 和 T2 同时执行 put / remove 等结构性修改(structural modification)的操作。以 put 方法为例分析,会发生元素覆盖。 Q2: 链表和红黑树的转换阈值为什么是 8 和 6 ? 首先分析下为什么会有链表和红黑树。理想情况下,HashMap 中每个 bin 所在位置只有一个节点,这样查询效率最高,为 O(1)。 参考链接: https://www.jianshu.com/p/7af5bb1b57e2 相关阅读: JDK源码分析-HashMap(1) Stay hungry, stay foolish.
1、两者父类不同 HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。 2、对外提供的接口不同 Hashtable比HashMap多提供了elments() 和contains() 两个方法。 HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。 4、安全性不同 HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。 虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的使用场景都是单线程。
目录 1.HashMap的数据结构? 2.HashMap的工作原理? 3.当两个对象的hashCode相同会发生什么? 4.你知道hash的实现吗?为什么要这样实现? 5.为什么要用异或运算符? 1.HashMap的数据结构? 哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点。当链表长度超过8时,链表转换为红黑树。 2.HashMap的工作原理? 我们在创建HashMap的时候,阿里巴巴规范插件会提醒我们最好赋初值,而且最好是2的幂。 答:hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 – 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap ”的问题; HashMap 的长度为什么是2的幂次方 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。
前言 HashMap 是我们熟悉的散列表实现,也是 “面试八股文” 的标准题库之一。今天,我给出一份 HashMap 高频面试题口述简答答案,希望对你刷题有帮助。 、队列 4、栈 5、二叉树(高频面试题与解题框架)[2] 6、图 二、算法思想: 1、回溯算法解题框架[3] 2、二分查找解题框架[4] 3、排序算法 4、贪心算法解题框架 5、动态规划解题框架 三、高级数据结构 、并查集(动态连通问题)[9] 8、二叉堆(优先队列)[10] 四、刷题题解: 1、高楼丢鸡蛋(动态规划)[11] 2、计算器(逆波兰表达式)[12] 3、链表(高频面试题)[13] 4、相交链表 & 环形链表(高频面试题)[14] ---- 1. 例如,HashMap 在达到阈值时执行扩容,本质上是扩大了输出值域。 ---- 2. HashMap 设计思路 2.1 说一下 HashMap 的底层结构?
2 构造函数 // 默认构造函数。 static final int DEFAULT_INITIAL_CAPACITY = 16; // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换) static = null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue 的实际大小 不小于 “阈值”,则调整HashMap的大小 if (size++ >= threshold) resize(2 * table.length); // 在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”; // 因此,需要调用addEntry() // 2.createEntry() 一般用在 新增Entry
这篇文章仅限小编个人的理解,小编不是Java方向的,只是对Java有很高的学习兴趣 如果有什么不对的地方还望大佬指点 HashMap的底层是数组+链表,(很多人应该都知道了) JDK1.7的是数组 8的时候,会变成红黑树 在红黑树的元素小于6的时候会变成链表 元素进行尾插 HaspMap的数组默认大小为16 数组也叫做Hash桶 (貌似听说这个值和阿里巴巴Java开发手册好像有点关系) HashMap 元素的下标是 HashCode(元素) & (数组的长度-1) HashMap的扩容 Resize 扩容的话,这里有一个值叫做loadFactor(阈值),默认值为0.75; 当数组的 元素数量>数组大小 , JDK1.7的是分段数组,有Segment锁(继承于ReentrantLock)加速一小段保证并发 JDK1.8 是和HashMap一样了,数组+链表(或者红黑树) Synchronized( 这一点与乐观锁,SVN的思想是比较类似的) 使用HashTable(基本是废弃的) HashTable就是把HashMap套上了一个Synchronized Collections.synchronizedMap
JAVA系列面试题 特点 1.高频面试 2.力求精深 3.贴近企业 4.迭代升级 1.HashMap的数据结构 基本功的问题,难度指数:1星; 1.7 数组+链表; 1.8 数组+链表+红黑树 2.当两个对象的 在HashMap这种数据结构上,存储的元素,hashCode一样,如果说,内容一致,则不能添加到同一个HashMap对象;否则,内容不一致,在此结构之后,变成链表格式。 3.初始容量是多少? 基本功,难度指数:2星 首先是HashMap数组的存储结构,在hashCode()冲突的时候,长度够用情况下,变成链表格式; 其次:长度》=8,最小容量是64的情况下,会变成红黑树。 基本功,难度指数:2星 负载因子:加载因子,来自于数学“泊松分布”。 16*2=32。容量2倍。 详见代码。 6.如何一次性设置1024元素,不扩容? 难度指数:3星-4星 考察知识点:HashMap基本功。
HashMap和Hashtable的区别 1.共同点:都是双列集合,底层都是哈希算法 2.区别: * 1.HashMap是线程不安全的,效率高,JDK1.2版本 * Hashtable是线程安全的 也只有这些大师级人物才能写出HashMap这么大道至简的数据类型了。 2 产生时间 Hashtable是java一开始发布时就提供的键值映射的数据结构,而HashMap产生于JDK1.2。 8 初始容量大小和每次扩充容量大小的不同 Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。 而HashMap则更加关注hash的计算效率问题。在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。 HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。
HashMap 说到 HashMap 想必大家从脑海中直接复现出了一大堆的面试题, HashMap 的数据结构 JDK7 和 JDK8 HashMap哪里不一样 HashMap是否安全 HashMap 的扩容机制 说到这里,我们就来挨着分析一下这个 HashMap 的这写面试题。 HashMap 里面有几个比较重要的参数: //默认初始容量——必须是2的幂 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //当没有构造函数中指定使用的负载系数 HashMap是否安全 一说这个,肯定都是非常基础的面试题,都知道 HashMap 是属于那种线程不安全的类,为什么不安全,他不安全到底会提现在哪个地方,难道面试的时候,你就只会说他的内部没有被 synchronize MAXIMUM_CAPACITY : n + 1; } 而 tableSizeFor 这个方法是用于计算出大于等于 cap 值的最大的2的幂值,而后续 HashMap 需要扩容时,每次 table
因此,在键或值的基础上排序HashMap是一个很难的面试问题,如果你不知道如何解决的话。下面让我们看看如何解决这个问题。 ? 1. HashMap存储每对键和值作为一个Entry<K,V>对象。 例如,给出一个HashMap, ? 键的每次插入,都会有值对应到散列映射上,生成一个Entry <K,V>对象。通过使用这个Entry <K,V>对象,我们可以根据值来排序HashMap。 2.创建一个简单的HashMap,并插入一些键和值。 ? 3.从HashMap恢复entry集合,如下所示。 ? 4.从上述mapEntries创建LinkedList。 7. ele1.getValue(). compareTo(ele2.getValue())——比较这两个值,返回0——如果这两个值完全相同的话;返回1——如果第一个值大于第二个值;返回-1——如果第一个值小于第二个值 由于HashMap不保持顺序,因此我们要使用LinkedHashMap。 ? 10.完整的代码如下。 ? ? ?
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供外同步。 Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。 就HashMap与HashTable主要从三方面来说。 1.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java + 1.2引进的Map接口的一个实现 2.同步性:Hashtable是线程安全的,也就是说是同步的 ,而HashMap是线程序不安全的,不是同步 3.值:只有HashMap可以让你将空值作为一个表的条目的key或value
容量: 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。 之后每次扩充,容量变为原来的 2 倍。 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小。 HashMap 的长度为什么是 2 的幂次方 HashMap底层使用的是哈希表(链表加数组)存储时可以通过运算后得出自己在数组中所存储的位置。 的长度是2的整数次幂? 由于桶的长度是2的n次方,这么做其实是等于 一个模运算。
HashMap的底层数据结构是数组+链表+红黑树,数组的作用显而易见,时间复杂度最低O(1),默认大小是16,数组的下标索引是通过key的hashcode计算出来的,当多个key计算出的hashcode 相同时,数组元素就会转化为链表,时间复杂度升为O(n),当链表的长度大于8并且数组的大小超过64时,链表会转化为红黑树,时间复杂度为O(log(n)),从源码角度来分析下HashMap的几个核心方法。
序言 在后端的日常开发工作中,集合是使用频率相当高的一个工具,而其中的HashMap,则更是我们用以处理业务逻辑的好帮手,同时HashMap的底层实现和原理,也成了面试题中的常客。 在看到下面的解释前,大家可以先思考下~ 在文档开头,给出了HashMap类中的各个域变量。其中,哈希table的初始大小默认设置为16,为2的次幂数。后面在扩容时,都是以2的倍数来扩容。 一般的扩容分为2步,第1步是对哈希表长度的扩展(2倍),第2步是将旧table中的数据搬到新table上。 那么,在JDK8中,HashMap是如何扩容的呢? 但有个缺点就是对素数取模的性能较低(涉及到除法运算),而HashMap的长度都是2的次幂,设计就较为巧妙,前面章节也提到过,这种方式的取模都是直接做位运算,性能较好。 Reference 1、Java 8系列之重新认识HashMap: https://tech.meituan.com/2016/06/24/java-hashmap.html2、fail-fast是个什么策略
= e.getKey(); if (k1 == k2 || (k1 ! = null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 ! = null && v1.equals(v2))) return true; } return false; } // 实现 的实际大小 不小于 “阈值”,则调整HashMap的大小 if (size++ >= threshold) resize(2 * table.length); } addEntry
对于字符串“String”来说,头部(8+4)+数组长度(4)+“String”(2*6)+padding(4)=32字节 因此,它的总占用空间为56字节 ArrayList 类结构图 ? ? HashMap内部结构比较复杂,除了一些基本的类型,还有比较复杂一点的集合类型。 以 Map<String,String> map = new HashMap<String,String>(); 这时候我们计算一下他的占用空间情况: ? (4)=48字节 table:头部(8+4)+长度(4)=16字节 然后我们put进去一条数据:map.put( "100002", "张明"); 当HashMap初始化的时候,他会开辟一个长度为16 hashmap:头部(8)+int(4*4)+float(4)+table数组引用(4)+entrySet引用(4)+keySet引用(4)+values引用(4)+padding(4)=48字节 table
关于HashMap的详解文章请移步: 链接: HashMap源码研究——源码一行一行的注释 文章目录 为什么初始容量是 2次幂? 如果指定了不是2的次幂的容量会发生什么? /红黑树越大,从而降低Hashmap 的性能。 答案:会获得最接近的一个2的次幂作为容量 有一个初始容量参数的构造方法HashMap(int initialCapacity) 参数:initialCapacity 初始容量 public HashMap 总结 总的来说,不管是规定 Hashmap 的 n 为 2次幂,还是扰动函数,都是为了一个目标,降低哈希冲突的概率,从而使 HashMap 性能得到优化。 而规定 n 为 2次幂,是在新建 Hashmap对象初始化时,规定其容量大小的角度来优化。而扰动函数是插入 key 值时改变 key 的散列值来达到优化效果。
做除余的时候2的倍数可以直接使用&进行快速计算 例如: 19%16可以写成19&(16-1),位运算更高效 扩容的时候只移动大约一半的数据,并且不会造成扩容之后碰撞更加严重的情况 例如: hash 值为4和8的值存放在size为4的数组中,则两个元素都存放在0下标的数据中,当以2倍扩容时,size变为8,8依然存放在0下标位置上,而4移动到下标为4的位置上,这样不仅达到了扩容的效果,还减少了hash