概述
1 | public HashMap(int initialCapacity, float loadFactor) { |
initialCapacity 初始容量(默认16):hashMap底层由数组实现+链表(或红黑树)实现,但是还是从数组开始,所以当储存的数据越来越多的时候,就必须进行扩容操作,如果在知道需要储存数据大小的情况下,指定合适的初始容量,可以避免不必要的扩容操作,提升效率
threshold 阈值:hashMap所能容纳的最大价值对数量,如果超过则需要扩容,计算方式:threshold=initialCapacity*loadFactor(构造方法中直接通过tableSizeFor(initialCapacity)方法进行了赋值,主要原因是在构造方法中,数组table并没有初始化,put方法中进行初始化,同时put方法中也会对threshold进行重新赋值,这个会在后面的源码中进行分析)
loadFactor 加载因子(默认0.75):当负载因子较大时,去给table数组扩容的可能性就会少,所以相对占用内存较少(空间上较少),但是每条entry链上的元素会相对较多,查询的时间也会增长(时间上较多)。反之就是,负载因子较少的时候,给table数组扩容的可能性就高,那么内存空间占用就多,但是entry链上的元素就会相对较少,查出的时间也会减少。所以才有了负载因子是时间和空间上的一种折中的说法。所以设置负载因子的时候要考虑自己追求的是时间还是空间上的少。(一般情况下不需要设置,系统给的默认值已经比较适合了)
Initial capacity The capacity is the number of buckets in the hash table, The initial capacity is simply the capacity at the time the hash table is created.
Load factor The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.
Capacity就是buckets的数目,Load factor就是buckets填满程度的最大比例。如果对迭代性能要求很高的话不要把
capacity
设置过大,也不要把load factor
设置过小。当bucket填充的数目(即hashmap中元素的个数)大于capacity*load factor
时就需要调整buckets的数目为当前的2倍。
put函数的实现
put函数大致的思路为:
- 对key的hashCode()做hash,然后再计算index;
- 如果没碰撞直接放到bucket里;
- 如果碰撞了,以链表的形式存在buckets后;
- 如果碰撞导致链表过长(大于等于TREEIFY_THRESHOLD),就把链表转换成红黑树;
- 如果节点已经存在就替换old value(保证key的唯一性)
- 如果bucket满了(超过load factor*current capacity),就要resize。
1 | public V put(K key, V value) { |
get函数的实现
- bucket里的第一个节点,直接命中;
- 如果有冲突,则通过key.equals(k)去查找对应的entry
若为树,则在树中通过key.equals(k)查找,O(logn);
若为链表,则在链表中通过key.equals(k)查找,O(n)。
1 | public V get(Object key) { |
RESIZE的实现
当put时,如果发现目前的bucket占用程度已经超过了Load Factor所希望的比例,那么就会发生resize。在resize的过程,简单的说就是把bucket扩充为2倍,之后重新计算index,把节点再放到新的bucket中。
1 | final Node<K,V>[] resize() { |