1、Hashmap类图结构
HashMap继承于AbstractMap,实现了Map,Cloneable,Serializable接口
2、HashMap数据结构
HashMap是使用数组加链表实现,jdk1.8之后当链表个数为8的时候,链表就会转换为红黑树(平衡二叉树)
3、HashMap重要参数
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
HashMap默认初始容量为16
static final int MAXIMUM_CAPACITY = 1 << 30;
HashMap最大容量为1的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f
HashMap的加载因子为0.75,即当hashmap内存储元素超过hashmap此时容量的0.75,hashmap就会进行扩容
为什么为0.75? 通常加载因子需要在时间和空间成本上寻求一种折衷,加载因子过高,例如为1,虽然节省了空间开销,提高了空间利用率,但同时也增加了查询时间的成本;加载因子过低,例如为0.5,虽然可以减少查询时间的成本,但是空间利用率很低,同时提高了rehash操作的次数。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子
,以便最大限度地减少rehash的操作次数,所以一般在使用hashmap创建时建议根据预估值设置初始容量,减少扩容操作。选择0.75为加载因子,完全是时间和空间成本上折衷的选择。
static final int TREEIFY_THRESHOLD = 8;
Hashmap一个链表元素个数当超过1.8之后就会进行转红黑树(平衡二叉树)。
为什么为8?
这在HashMap源码中给出了解释,理想情况下使用随机的哈希码,容器中节点分布在hash桶中的频率遵循泊松分布,按照泊松分布计算出的对照表,当为8时,概率就很小了(0.00000006),所以就采用了8
4、HashMap方法分析
在介绍HashMap常用方法源码分析前,我们先了解下HashMap内的hash函数
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
hashcode产生的是一个int类型的数据,长度最大为32位,h>>>16即取了key hashcode的低16位
4.1 HashMap的put
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断tab是不是为空,如果是是空,那么就进行HashMap初始化 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length;//n为HashMap的容量 if ((p = tab[i = (n - 1) & hash]) == null)
//通过 i=(n-1) & hash运算刚好可以得到一个0到n-1的整数,刚好在hash桶的范围内
//假设n=16 n-1=15 15表示为0000000000001111 hash为00001001010010110 通过&运算 同1才为1,进行运算后肯定在0-15之中
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}