HashMap源码中的一些重要常量
1. DEFAULT_INITIAL_CAPACITY: HashMap的默认容量16
2. MAXIMUM_CAPACITY: HashMap的最大支持容量,2^30
3. DEFAULT_LOAD_FAC TOR: HashMap的默认加载因子:0.75
4. TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树:8
5, UNTREEIFY_THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表
6. MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64.(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
7. table:存储元素的数组,总是2的n次幂entrySet:存储具体元素的集
8. size: HashMap中存储的键值对的数量
9. modCount: HashMap扩容和结构改变的次数。threshold:扩容的临界值,=容量*填充因子
10. loadFactor:填充因子
jdk7
在实例化之后,创建了一个长度为16的一维数组Entry[] table
当有一个数据要添加时(key-value),调用key所在类的HashCode()方法计算出key的哈希值,经过某种算法后算出此哈希值所在数组中的位置。
如果没有数据,则添加成功
若有数据(意味着此位置有一个或多个数据,这些数据以链表形式存在),则依次比较链表中的key值与要添加的key值的哈希值是否一样。
若不一样,则添加成功
若一样,则比较两个的内容是否相同,若相同,则覆盖原有数据。若不相同,则添加成功
注意:在不断的添加过程中会涉及到扩容问题,大小为扩容为原来的两倍,并将原有数据复制到扩容后的数组中
jdk8
相对于jdk7在底层方面的不同
1. new HashMap() 底层没有创建一个长度为16的数组
2, jdk8底层数组是Node[],而非Entry[]
3. 首次调用put()方法时,底层创建长度为16的数组
4. jdk7底层结构只有数组+链表。jdk8中底层结构:数组+链表+红黑树。当数组的某一个索引位置上的元素以链表形式存在的数据个数大于8且当前数组的长度大于64。此时此索引位置上的所有数据改为使用红黑树存储
jdk8中HashMap源碼解析
当首次创建一个HashMap实例时:HashMap map = new HashMap(); 并不会创建数组
publicHashMap() {
// 只会初始化一个负载因子,默认大小为: static final float DEFAULT_LOAD_FACTOR = 0.75f;
this.loadFactor=DEFAULT_LOAD_FACTOR;// all other fields defaulted
}
当调用put方法时会传入如下参数
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
其中hash(key)便是根据传入的key值计算出哈希值,计算方法如下
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
当进入putVal()方法时,首先会先声明四个变量:Node<K,V>[] tab; Node<K,V> p; int n, i;接着会进行判断是否需要初始化。代码如下
if ((tab = table) == null || (n = tab.length) == 0){
n = (tab = resize()).length;
}
当条件中两个条件中的任何一个条件满足,就会进行初始化。调用resize()方法进行初始化
table意思:该表在首次使用时初始化,并根据需要调整大小。分配时,长度始终是 2 的幂。我们还在某些操作中允许长度为零,以允许当前不需要的引导机制。
初始化过程在resize()方法中,首先会执行如下过程
newCap = DEFAULT_INITIAL_CAPACITY; // static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 0.75*16=12
然后会把newThr赋值给临界值threshold,接着创建一个长度为16的数组。并把创建好的数组赋值给table,这样在下次执行put方法时,判断不为空,就不会执行初始化部分。
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
最后返回创建出来的数组
return newTab;