转载于:https://blog.csdn.net/Yoga0301/article/details/84452104
HashMap的实现采用了除留余数法形式的哈希函数和链地址法解决哈希地址冲突的方案。这样就涉及到两种基本的数据结构:数组和链表。数组的索引就是对应的哈希地址,存放的是链表的头结点即插入链表中的最后一个元素,链表存放的是哈希地址冲突的不同记录。
实现
当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾,数组中存储的是最后插入的元素 。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
常见问题
-
HashMap底层是如何实现的?
首先底层数据结构是由数组+链表组成链表散列。
a) HashMap先得到key的散列值,在通过扰动函数(减少碰撞次数)得到Hash值,接着通过hash & (n -1 ),n位table的长度,运算后得到数组的索引值。
b) 如果当前节点存在元素,则通过keys.equals()
,相等则替换,不相等则通过拉链法查找元素,直到找到相等或者下个节点为null时。
c)1.8对扰动函数,扩容方法进行优化,并且增加了红黑树的数据结构。 -
当两个对象的hashcode相同怎么办
当哈希地址冲突时,HashMap采用了链地址法的解决方式,将所有哈希地址冲突的记录存储在同一个线性链表中。具体来说就是根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。 -
如果两个键的hashcode相同,你如何获取值对象 ?
HashMap在链表中存储的是键值对,找到哈希地址位置之后,会调用keys.equals()
方法去找到链表中正确的节点,最终找到要找的值对象 -
如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办 ?
HashMap默认的负载因子大小为0.75,也就是说,当一个map填满了75%的空间的时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的数组,来重新调整map的大小,并将原来的对象放入新的数组中。 -
HashMap的长度为什么是2的幂数
在HashMap的操作流程中,首先会对key进行hash算法得到一个索引值,这个索引值就是对应哈希桶数组的索引。为了得到这个索引值必须对扰动后的数和数组长度进行取余运算。即 hash % n
(n为hashmap的长度),又因为&比%运算快。n如果为2的倍数,就可以将%转换为&,结果就是 hash & (n-1)。所以这就解释了为什么HashMap长度是2的倍数。
注:若h%n,当n是2的幂数时,有h%n=h&(n-1)
- HashMap 和 Hashtable 的区别
线程安全:HashMap是线程不安全的,而HashTable是线程安全的,每个人方法通过修饰synchronized来控制线程安全。
效率: HashMap比HashTable效率高,原因在于HashTable的方法通过synchronized修饰后,并发的效率会降低。
允不允许null: HashMap可以允许key和value都为null值,而Hashtable不允许key和value为null。