一、结构
jdk7中的Hashmap是由链表和数组实现的。
每次进行put操作,实际上是将key和value封装成了一个entry对象放入数组中。
进行put操作时,先求出key的hashcode值,然后根据数组长度进行一系列的算法运算(源码中的 indexFor 方法),得出下标后放入数组中。若求得的下标相同,就需要用到链表结构。
链表结构有两个基本属性,一个内容content,一个下一节点next,每个节点都通过next相连,其中,若要进行插入操作,将新内容作为头部节点效率最高。插入完成后,因为链表插入的都是头部节点,所以如果还按照之前的数组结构去取节点的话,会取不到当前节点的头部节点,故,hashmap在插入完成后会把链表往下移,保证链表的头部在数组中,hashmap的数组中存的的是引用数据类型entry的引用地址,entry并不是直接放在数组中,而是在堆中,所以,把链表往下移的操作实际上就是把当前数组节点存放的引用地址改为链表头部节点的引用地址。
{
int hashcode=key.hashcode(); //算出哈希值
int index=hashcode % table.length; //算出下标位置
//若下标位置没有数据存在
table[index]=new Entry(key,value,null);
//若下标位置有数据存在
table[index]=new Entry(key,value,table[index]);
}
数组table默认长度为16,加载因子为0.75,长度乘以加载因子的值是一个阈值,当数组长度超过这个阈值,数组就会扩容。
二、特殊性
hashmap的key是允许为null的。
hashmap中的key是唯一的,在调用put方法时,会进行一个循环查找是否已经有这个key,若插入重复的key,则覆盖之前的value,且put方法会返回之前的value值。
hashmap的扩容机制会生成一个新的数组,长度是原来的两倍,然后将原来的数组重新计算hash后插入新的数组中,原来的值的位置可能会发生变化,有两种情况,第一种为位置不变,第二种为位置是 原来的位置+原来的数组长度
hashmap是线程不安全的,多个线程操作同一个hashmap,会在扩容时发生异常,导致循环链表出现。
三、modcount机制
对hashmap进行增删改操作时,会对modcount进行+1操作,其目的是为了对多个线程操作同一个hashmap时避免一边遍历一边增删导致的数据错误问题,迭代器在遍历时,会判断迭代器被创建时的modcount和当前的modcount是否相等,不相等就会抛出异常,这也是个fast-fail机制。