1、数据结构初始化
HashMap是数组与链表两种数据结构的组合,初始化HashMap的时候首先初始化一个数组Entry,该数组中的每个元素是一个静态内部类Entry
/**
* An empty table shared by all zero-capacity maps (typically from default
* constructor). It is never written to, and replaced on first put. Its size
* is set to half the minimum, so that the first resize will create a
* minimum-sized table.
*/
private static final Entry[] EMPTY_TABLE
= new HashMapEntry[MINIMUM_CAPACITY >>> 1];
/**
* Constructs a new empty {@code HashMap} instance.
*/
@SuppressWarnings("unchecked")
public HashMap() {
table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
}
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
final int hash;
Entry<K,V> next;
..........
}
2、put方法
HashMap调用put(“key”,”value”)方法的时候,首先根据传入的key计算出相对应的hash值,再将得到的hash值与(数组长度-1)进行&运算,得到这个key对应的Entry元素在数组中下标。如果该下标处已经有元素存在,判断该元素与已经存在的元素的key值是否相同。如果相同,就将该处key所对应的value值替换,key不变;如果key不相同,但是key对应的hash值相同,那么在数组中同一位置上的元素将以链表的形式存储,最先进入该位置的放到链表的尾部,新加入的放到链表的头部。
/**
* Maps the specified key to the specified value.
*
* @param key
* the key.
* @param value
* the value.
* @return the value of any previous mapping with the specified key or
* {@code null} if there was no such mapping.
*/
@Override public V put(K key, V value) {
if (key == null) {
return putValueForNullKey(value);
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
preModify(e);
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
// No entry for (non-null) key is present; create one
modCount++;
if (size++ > threshold) {
tab = doubleCapacity();
index = hash & (tab.length - 1);
}
addNewEntry(key, value, hash, index);
return null;
}
3、get()方法
HashMap调用get(“key”)方法的时候,同样首先计算出key所对应的hash值,根据hash值找到key对应的Entry元素在数组中的位置,然后通过key的equals方法,在该位置处找到相应的元素。
/**
* Returns the value of the mapping with the specified key.
*
* @param key
* the key.
* @return the value of the mapping with the specified key, or {@code null}
* if no mapping for the specified key is found.
*/
public V get(Object key) {
if (key == null) {
HashMapEntry<K, V> e = entryForNullKey;
return e == null ? null : e.value;
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return e.value;
}
}
return null;
}
4、HashMap扩容
HashMap初始化时默认的capaticy为16,loadFactor为0.75,当HashMap的容量大于16*0.75=12的时候,HashMap需要进行数组扩容,数组大小变为16*2=32,即扩展为原来的一倍。然而当数组长度发生变化时,HashMap中的各个元素的位置也会发生变化,原因是,元素位置是根据key对应的hash值与数组长度-1进行&操作得到的,扩容后,数组长度发生改变,则需要重新计算每一个元素在数组中的位置,这是一个非常耗时的操作,所以如果我们预先知道HashMap中元素的个数,通过改变capacity或loadFactory(一般不改变这个值)来避免HashMap出现扩容,以提高HashMap的工作效率。如:已知元素有60个,为了避免扩容,让capacity*0.75>60,且capacity为2的n次方,则capacity应该取
128更合适,即new HashMap(128);
/**
* Constructs a new {@code HashMap} instance with the specified capacity and
* load factor.
*
* @param capacity
* the initial capacity of this hash map.
* @param loadFactor
* the initial load factor.
* @throws IllegalArgumentException
* when the capacity is less than zero or the load factor is
* less or equal to zero or NaN.
*/
public HashMap(int capacity, float loadFactor) {
this(capacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Load factor: " + loadFactor);
}
/*
* Note that this implementation ignores loadFactor; it always uses
* a load factor of 3/4. This simplifies the code and generally
* improves performance.
*/
}
总结:本文主要讲了HashMap的数据存储结构、put、get方法、以及HashMap扩容的一些基本原理。