这一章节我们来介绍HashMap的工作原理。
1.HashMap的工作原理图
下图引用自:http://www.admin10000.com/document/3322.html
2.HashMap初始化的时候我们可以这样理解:一个数组,每一个位置存储的是一个链表,链表里面的每一个元素才是我们记录的元素
3.下面我们来看put的源码:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
解释:
a.当key为空的时候,通过putForNullKey方法,把元素放到最开始的位置。注意:HashMap是允许Key为空的。下面的代码就是证明:
package com.ray.ch15;
import java.util.HashMap;
public class Test {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(null, "1");
}
}
b.当key不为空的时候,需要计算key的hashcode,因为hashCode()方法是继承Object,因此每一个key都有这样的方法。当然,我们也可以重写hashCode(),但是这里将出现一系列的问题,我们后面的章节再展开。
注意:其中有一个hash()方法,就是把Key的hashCode再一次hash,这样做主要是为了使这个hash码更加的平均分布,下面是hash的源码:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
然后我们再通过indexFor计算出这一系列元素放在大table的什么位置。
d.当计算完链表在table上面的位置,我们就需要遍历上面的元素,因此出现了for循环。
扩展(1):这里需要突出一下上面所说的table,table的源码:
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;
它是一个Entry组成的数组,这个Entry就是我们放到里面的元素。
这个table,就是我们上图的左边的Y轴上面的1,2,3,4.....它每一个位置上都是记录一个Entry的链表。
从注释可以看到,这个table是可变的,当Map的容量超过某一个界限,他就会触发resize方法,从新创建一个table,然后把就的数据copy一份到新的里面去。下面是resize的源码:
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
扩展(2):讲到table,我们必须将另一额重要参数装载因子(DEFAULT_LOAD_FACTOR)。下面是装载因子初始化的值,0.75,代表当Map的容量达到0.75的时候,将触发resize方法,重新扩展Map的大小。这个因子的数值决定了一大部分Map 的性能,后面的章节将会说明。
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
4.综上所述,Map的put 的流程是:
(1)检查key是否为空
(2)计算key的hashcode和在table里面的index(位置)
(3)找到table上面的元素
(4)遍历链表,如果没有就put进去,有就更新
总结:这一章节我们主要通过put方法来介绍HashMap的工作原理。
这一章节就到这里,谢谢。
-----------------------------------