假设一段代码:
HashMap<String, String> map = new HashMap<String, String>();
map.put(“张三”, “测试数据”);
map.put(“李四”, “测试数据”);
一、HashMap底层数据结构最核心的是数组,简单的理解如:
对张三这个key,计算出hash值,对hash值进行取模处理,定位到数组里的一个元素中去
[<>, <>, <>, <张三, 测试数据>, <李四, 测试数据>, <>, <>, <>]
二、JDK1.8中对hash算法和寻址算法的优化
如:
map.put(“张三”, “测试数据”);
对”张三”这个key计算它的hash值,是有一定优化的:
static final int hash(Object key){
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
先拿到key的hashcode,再与二进制右移16位后的值进行异或运算;
寻址算法的优化:
(n - 1) & hash
数组的长度减1后,与hash值的二进制进行与运算,得到数组中的一个位置;
三、HashMap是如何解决hash碰撞问题的
hash算法的优化可以在一定程度上避免hash冲突;
put的时候,当两个key或者多个key,定位出的在数组中的位置还是一样的,就会出现hash碰撞的情况;
这时候单单采用数组就不能解决问题了,会在数组中的该位置挂上一个链表,在链表里面放入多个元素,让多个key-value对同时放在数组的一个位置里,从而有效解决了hash冲突的问题。
get的时候,如果定位到数组里,发现这个位置挂了一个链表,则只需遍历这个链表,从中找到自己要找的那个key-value对即可。
假设你的链表长度过长时,可能会导致遍历链表的性能比较差,
解决办法是当链表的长度达到一定的上限时,就会把链表转换为红黑树,好处在于遍历红黑树查找元素的时间复杂度为O(logn),性能相较于链表会有所提高。
四、HashMap的扩容机制
由于HashMap底层是一个数组,所以当这个数组满了以后,就不可避免地要涉及到数组扩容的问题,变成一个更大的数组,让你在里面可以放更多的元素;
默认的扩容机制是2倍扩容;
进行扩容之后,要进行Rehash,就是把扩容前的数组中的元素重新寻址定位到扩容后新的数组位置上去。