1. 将loadFactor设置为0.75,threshold设置为12,并创建一个大小为16的Entry对象数组。
2. 可通过调用HashMap的另外2个构造器来控制初始的容量值及loadFactor,至于创建的Entry对象数组的大小,并非传入的初始容量值,而是采用如下方法来决定的:
public HashMap(int initialCapacity, float loadFactor) {
// Find a power of 2 >= initialCapacity
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
}
Capacity才是真正的创建的Entry对象数组的大小,即真实的Entry对象数组的大小应为大于initialCapacity的2的倍数。
3. put(Object key, Object value)
对于key为null的情况,HashMap的做法为获取Entry数组中的第一个Entry对象,并基于Entry对象的next属性遍历,当找到了其中Entry对象的key属性为null时,则将其value赋值为新的value,然后返回。如没有key属性为null的Entry,则增加一个Entry对象,增加时为先获取当前数组的第一个Entry对象:e,并创建对象,key为null,value为新传入的对象,next为之前获取的e,如此时Entry数组中已用的大小>=threshold,则将Entry数组扩大为当前大小的2倍,扩大时,对当前Entry对象数组中的元素重新hash,并填充数组,卒后重新设置threshold值。
对于key不为null的情况,首先获取key对象本身的hashcode,然后再对此hashCode做hash操作。
Hash完毕后,将hash出来的值与Entry对象数组的大小减1的值进行按位与操作,从而得出当前key要存储到数组的位置。
从这个过程可以看出,可能会出现不同的key找到相同的存储位置的问题,也就是数据结构中经典的hash冲突的问题了。
下面是HashMap解决冲突的办法:
在找到要存储的目标数组的位置后,获取该数组对应的Entry对象,通过调用Entry对象的next属性来进行遍历,寻找hash值和计算出来的hash值相等、且key相等或equals的Entry对象,如寻找到,则替换此Entry对象的值,完成put操作,并返回旧的值;如未找到,则往对应的数组位置增加新的Entry对象,增加时采取的方法和key为null的情况基本相同,只是它是替换指定位置的Entry对象,可以看出,Hash解决hash冲突采用的是链表的方式。
4. get(Object key):
get的过程和put一样,也是根据key是否null来分别处理的。
对于key为null的情况,则直接获取数组中第一个Entry对象,并基于next属性进行遍历,寻找key为null的Entry对象,如找到则返回Entry对象的value值,如未找到,则返回null。
对于key为非null的情况,则对key进行hash和按位与操作,找到其对应的存储位置,然后获取此位置对应的Entry对象,基于next属性遍历,寻找到hash值相等,且key相等或equals的Entry对象,返回其value,若未找到,则返回null。
5. remove(Object key):
Remove的过程和get类似,只是在找到匹配的key后,如数组上的元素等于key,则将数组上的元素的值置为其next元素的值;如数组上的元素不等于key,则对链表遍历,一直到找到匹配的key或链表的结尾。
6. 总结:
原文:http://www.cnblogs.com/chy2055/p/5207413.html