标签:hashmap原理
1. 数据存储、类型:数组(table):没有冲突的数据存放至数组中。
链表:冲突的数据形成一条单向链表,先put的在尾部后put的在头部,头部存至table中。
数据类型:java.util.HashMap.Entry<K, V> implements Map.Entry<K,V>,属性有:key、value、next、hash。
2. 加载因子、threshold、扩容:
加载因子:哈希表在其容量自动扩容之前可以达到多满的一个阈值,初始值为0.75。
threshold=容量 * 加载因子。
扩容:扩容后的table长度为原有的2倍,扩容条件如下:
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
} 影响HashMap 性能的两个参数:初始容量和加载因子,尤其加载因子,如果加载因子过低就会增加table扩容次数并且数组利用率降低。如果过高则降低其查找性能(put、get)。
3. key的hash值的产生:
基于key的hashCode()方法产生数值,然后基于此数值计算得到最终的hash值;如果key为String类型则通过sun.misc.Hashing.stringHash32()方法直接产生最终hash值,源码如下:
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// 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);
}4. put:
根据key计算出hash值(当然了如果key为空特殊处理)。
根据hash值和table的长度计算此key应该存储至table的下标值index。
判断table的index元素是否为null:
如果为null,生成Entry存储至数组中下标为index的位置。
如果不为null并且hash值相等而且key也相同则用新的value替换老的value并返回老的value;如果key的hash值相等但是key不同则冲突发生,此时将形成一个链表,新来的作为头,之前的作为头的next。
相关源码如下:
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
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;
}
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length-1);
}
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 createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}5. get:
计算index的过程和put类似,然后判断table[index]是否为null如果是直接返回null,否则遍历以table[index]为头的链表,直到找到key的hash值相等并且key相同的Entry,然后返回其value值。相关源码:
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;
}6. key为null时只会存在table[0]开头的链表中。
标签:hashmap原理
原文地址:http://blog.51cto.com/wenshengzhu/2056128