1.7以前是:数组+链表
数组的元素是Map.Entiry对象
当出现哈希碰撞的时候,使用链表解决,
先计算出key对应的数组的下标,这个数组的这个位置上为空,直接放入,
若是不为空并且出现哈希碰撞,就把元素添加到链表的头部的,
new Entry(key,value,table[i]);这样这个Entry就是链表的头部了,而后放到数组的index位置上。java
1.8是:数组+链表+红黑树
数组的元素是Map.Node对象继承Map.Entry包含属性有:
当前node对象的hash值,
当前node对象的key,
当前node对象的下一个节点对象next,
当前node对象的valuenode
1.8为何使用数组+链表+红黑树?
由于若是一个hashmap在同一个数组位置上出现hash碰撞过多,那么这个链表的长度会很长,
插入块,可是查询由于使用的遍历因此会比较慢。git
由于计算key的下标的时候使用的是:key的hashcode值 &(按位与) 数组的长度-1 ,
固定为2的次幂能保证碰撞概率小。github
key的hashcode值 & 数组的长度-1 ;
& (与运算)面试
bucket里的第一个节点,直接命中;
若是有冲突,则经过key.equals(k)去查找对应的entry
若为树,则在树中经过key.equals(k)查找,O(logn);
若为链表,则在链表中经过key.equals(k)查找,O(n)。数组
哈希表在新增元素的时候首先会根据hash函数算出这个元素存放的数组的位置,
可是,有可能存在不一样的key值获得相同的数组位置(两个元素的HashCode值相同),这个时候就是哈希冲突。安全
动态数组+链表(单向)的方式。
若是某个位置上的链表很长,会影响检索,JDK1.8引入了当链表的长度大于8的时候会将链表动态替换为一个红黑树,增长了搜索性能。
等于说由:数组+链表
变成了数组+链表+红黑树
数据结构
当HashMapde的长度超出了负载因子与当前容量的乘积(默认16*0.75=12)时,
经过调用resize方法从新建立一个原来HashMap大小的2倍的newTable数组,
并将原先table的元素所有移到newTable里面,从新计算hash,而后再从新根据hash分配位置,,最大扩容为2的30次方+1。
负载因子默认是:0.75
多线程
多线程环境下,有可能多个线程同时进行resize,在这过程当中,可能产生死锁或者死循环。
具体缘由不清楚。并发
HashMap是线程不安全的,因此在多线程的环境中咱们须要寻找替代方案:
Fail-fast 机制是 java 集合(Collection)中的一种错误机制,
java.util.HashMap 不是线程安全的,若是在使用迭代器的过程当中有其余线程修改了 map,
那么将抛出 ConcurrentModificationException,这就是所谓 fail-fast 策略
这一策略在源码中的实现是经过 modCount 也就是修改次数实现的。
与之关联的面试题:
遇到过ConcurrentModficationException(并发修改异常)异常吗?为何会出现?如何解决?