HashMap保存数据的结构是数组+单向链表,它集成AbstractMap类以及实现Map接口:
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
//初始化大小16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大大小
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final Entry<?,?>[] EMPTY_TABLE = {};
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
/**
* The number of key-value mappings contained in this map.
*/
transient int size;//map中key-value个数
/**
* The next size value at which to resize (capacity * load factor).
* @serial
*/
// If table == EMPTY_TABLE then this is the initial capacity at which the
// table will be created when inflated.
int threshold;//当size值大于等于threshold时需要扩容
HashMap有四个构造方法:
1、带初始化大小和加载因子的构造方法
public HashMap(int initialCapacity, float loadFactor)
public HashMap(Map<? extends K, ? extends V> m)
2、带初始化大小的构造方法
public HashMap(int initialCapacity)
3、无参构造方法
public HashMap()
4、带Map类型参数的构造方法
public HashMap(Map<? extends K, ? extends V> m)
在HashMap中,通过key的hash值来计算元素在数组table中的位置
int hash = hash(key);
int i = indexFor(hash, table.length);
如果hash相等key也相等的话,则替换原来的值:
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;
}
}
如果不同的key计算出来的位置相同,则这些key-value组成单向链表,当实际的数据个数大于等于临界值threshold时,则需要以原大小的2倍的对table进行扩容:
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
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, initHashSeedAsNeeded(newCapacity));//重新计算位置并复制到新数组
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
并且需要重新计算数据再新数组中的位置并复制到新数组中。
通过key取value时,也是先通过key的hash计算位置,再遍历链表:
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
//遍历链表
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}