1、JDK1.8对HashMap和ConcurrentHashMap做了很大的修改,我们今天只对其JDK1.8的HashMap做初步的剖析
HashMap 主要的数据结构为数组+链表+红黑树
2、通过对源码进行分析,有几个重要的参数 默认的初始化数组大小为16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 最大的数组容量 static final int MAXIMUM_CAPACITY = 1 << 30; 装载因子,当hashmap的size达到数据容量*DEFAULT_LOAD_FACTOR时候,会进行扩容,每次扩容都扩一倍 static final float DEFAULT_LOAD_FACTOR = 0.75f; 为了解决hash冲突问题,hash值相同采用链表的形式,但是在1.8中,当链表长度超过8,会采用红黑树的数据结构,查找复杂度从n降为logn static final int TREEIFY_THRESHOLD = 8; 在删除或者扩容的时候,当数组某个index下的红黑树节点size下降到6以下,红黑树转换为链表 static final int UNTREEIFY_THRESHOLD = 6; 当数组的容量小于64时,当数据index下超过8个节点,不采用红黑树,而是扩容数组,当超过64,size没有达到数组容量的0.75时,一个index下超过8个才使用红黑树 static final int MIN_TREEIFY_CAPACITY = 64; 对源码做一些说明 key值的hash计算 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
根据key值的hashcode,与自身hash无状态右移16位做一个异或操作
static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
初始化的容量大小,hash希望大小都是2的幂次方,取大于等于初始化大小的最小2的幂次方,目的是为了hash映射的时候
(n - 1) & hash相当于hash对n取模,尽可能把hash映射到不同的数组index下,如果不是2的幂次方,有些槽位可能永远不会映射到。