HashMap就是数组加链表,不同的hash放到不同的索引上,相同的hash数据放到同一个索引处,但是并不equals,并将原索引处的数据放到新加入数据的成员变量entry上。
HashMap这个类,有个内部类叫做Entry<K,V>,存储了 key,value,hash,Entry<K,V> entry,就是key值,value值,hash值,跟下一个entry对象。
然后HashMap 有个最主要的成员变量存储所有数据 就是 Entry<K,V>[ ],数组里每个索引对应的hash值不同,如果hash值相同,存储于同一个索引的位置上,然后通过entry里记录的下个entry 这样的链表的形式存储。
执行put方法,首先判断是否添加的数据key为null,如果为null,添加在索引0位置上,再做处理,如果不是null,根据key算出hashCode再算出hash再算出index,遍历此index上的所有entry,如果有key值相同的,则将key对应的value更新,返回旧value并推出,否者执行添加entry方法。
添加entry方法首先判断这个数组的大小,如果不够大了。则将尺寸增大二倍,并且执行transfer转移方法,就是将所有数据,转移到新的entry[ ]里,遍历所有索引,在新数组里将原来位置上的entry放到新添加的entry的成员变量上,将新添加的entry放到索引处。如果够大,则将新的entry放到索引处,新的entry指向原索引处的entry。完成添加。
总结:
1、判断是否为空,为空放索引0上。
2、是否需要增大数据长度,如果需要转移数据。
3、计算添加数据的key值,经过一系列计算算出应该放置的索引位置,将此索引处数据放到新数据成员变 量上,并将新数据放到索引处。jdk1.7之前应该都是从链表的头部插入,1.8起从尾部插入。
执行get方法,首先判断输入的key是否为空,如果为空,则去索引0处取数据,如果有数据就返回,没有就返回null。(注意HashMap的key跟value都是可以为空的)
如果key不为空,判断这个HashMap的size是否为零,即是否有数据,如果没有返回null。
如果有数据,通过key的hashCode算出hash,在根据hash与Entry数组长度算出索引位置,然后遍历该索引上所有Entry,如果他们的hash相同,并且key相同,则返回这个entry对象。然后取出这个entry的value值。
**扩容:**图片中addEntry方法的第一行,只有容器内的存储的数据大于等于临界值并且要添加的数据要在数组的新索引上,才会扩容。如果只是不停地往已使用的索引上添加数据并不会扩容。
另外,关于几个参数。size就是实际存储的元素个数,存入多少不重复的key值,size就是多少。threshold临界值,loadFactor负载系数,一般来说entry数组的长度乘以系数等于临界值。然后size大于等于临界值就扩容,临界值跟着变大。与数组的长度成正比。
以及put方法中直接添加Map类型数据时,根据计算结果也可能扩容。