HashMap
初始化设置:
1、初始化默认容量:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
2、最大容量:2的30次方,和ArrayList相比ArrayList存放的元素个数多。
static final int MAXIMUM_CAPACITY = 1 << 30;
3、默认加载因子,如果构造方法没有传参的话,默认加载因子就是0.75。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
4、阀值,当一条链上的节点数增加且当增加到节点个数等于TREEIFY_THRESHOLD(10)时,就是将链表变成红黑树来保存。
static final int TREEIFY_THRESHOLD = 8;
5、阈值,当树上的节点削减且当削减到节点个数等于UNTREEIFY_THRESHOLD(6)时,红黑树会塌缩成为链表
static final int UNTREEIFY_THRESHOLD = 6;
6、一个hash节点上树的最小大小是64.
static final int MIN_TREEIFY_CAPACITY = 64;
7、实际存储的key,value的数组,只不过key,value被封装成Node了。
transient Node<K,V>[] table;
HashMap中的内部类——Node:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //注意这个成员变量
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
①注意Node类的成员成员成员变量:hash。
- 这个hash值是根据传来的key,算出各自的hash值,再进行异或处理计算出来的。
存放数据:
1、调用构造方法,让加载因子等于默认的加载因子(0.75)。
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
2、向HashMap中放置一个值
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
4、计算key值的hash值,如果key不为空的时候,hash值等于key自身的hash值做异或
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
注意:hash(key)是根据传进来的key值进行计算得出的。
5、放入元素之前的判断:resize()方法
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
threshold = newThr;
return newTab;
}
这里,由于oldTab(老的数组)此时指向table(null),所以oldCap(老的数组容量)为0,oldThr(老的临界值)也为0,然后让newCap(新的数组容量)为HashMap默认数组容量DEFAULT_INITIAL_CAPACITY(16),newThr(新的临界值)为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY(默认数组容量乘以默认转换因子:12),然后返回新的节点数组。
- 其实这个方法就是计算加载因子和阀值,如果当前元素个数达到了阀值,那么就会创建出来新的数组,然后把老的容器中的元素放到新的数组中去。
- 默认情况下,数组大小为16,那么当hashmap中元素个数超过16×0.75=12的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置。
6、向容器中放入数据的方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
}
这一句的意思是,当容器还是空的时候,插入数据的时候肯定要创建一个容器嘛,然后得到resize()创建的一个长度为16的Node数组。然后我们来看元素插入的那一条语句。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
根据key的hash和此时数组长度-1进行与运算,如果数组上此位置为空,那么就将元素直接放入数组中,
如果此时数组的元素不为空,那么根据拉链法,就会在原来数组元素上形成链表。
如果链表的长度等于阀值(8)的时候,那么就会形成红黑树来保存