HashMap源码解析
1、存储结构
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; }
}
2、属性
//默认容量,1向左移位4个,00000001变成00010000,也就是2的4次方为16 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //最大容量,2的30次方。 static final int MAXIMUM_CAPACITY = 1 << 30; //加载因子,默认0.75,扩容会用到。 static final float DEFAULT_LOAD_FACTOR = 0.75f; //当某个桶节点数量大于8时,会转换为红黑树。 static final int TREEIFY_THRESHOLD = 8; //当某个桶节点数量小于6时,会转换为链表,前提是它当前是红黑树结构。 static final int UNTREEIFY_THRESHOLD = 6; //当整个hashMap中元素数量大于64时,也会进行转为红黑树结构。 static final int MIN_TREEIFY_CAPACITY = 64; //存储元素的数组,transient关键字表示该属性不能被序列化 transient Node<K,V>[] table; //将数据转换成set的另一种存储形式,这个变量主要用于迭代功能。 transient Set<Map.Entry<K,V>> entrySet; //数组中存储K,V对的数量 transient int size;//临界值,也就是元素数量达到临界值时,会进行扩容。 int threshold; //也是加载因子,只不过这个是变量。 final float loadFactor;
3、构造方法
//无参构造方法 public HashMap() { //初始化加载因子为默认值(0.75) this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); }
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); }
//该方法会返回大于cap值的,且离其最近的2次幂,例如t为29,则返回的值是32 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; }
4、添加(修改)
//获取key的hashCode static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
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) { //tab 哈希数组,p 其中某一个哈希桶的首结点,n 表示哈希数组的长度, i 计算出来的数组下标 Node<K,V>[] tab; Node<K,V> p; int n, i; //引用哈希数组,获取其长度,如果table一开始没有进行加载的话(需要第一次put操作才能进行加载),或者哈希数组的长度为0,则进行扩容 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //如果计算出来的哈希桶的首结点没有值的话,则直接插入新结点key-value放到此处 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { //下面是发生hash冲突的几种情况 //e 表示临时结点的作用,k 表示存放当前结点的key值 Node<K,V> e; K k; //第一种,首节点的情况(p当前表示首结点,还没有移动过),如果key和value都相等,说明找到了 //value的两种比较,第一个表示值得对比,第二个表示对象的对比(前提是不能为null) 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) { //如果遍历到了链表尾部,说明链表中并没有重复的key-value,则直接在尾部添加新结点 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //判断该链表的个数是否大于8,如果是,则转换为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } //此处说明链表有重复的key值 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } //结合上面所说,有重复的key值,则进行覆盖操作 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; }
扩容:
final Node<K,V>[] resize() { //指向没扩容之前的哈希数组 Node<K,V>[] oldTab = table; //获取没扩容之前哈希数组的长度 int oldCap = (oldTab == null) ? 0 : oldTab.length; //获取没扩容之前哈希数组的临界值 int oldThr = threshold; //初始化新哈希数组的长度和临界值 int newCap, newThr = 0; //如果oldCap>0的话,说明不是首次进行初始化 if (oldCap > 0) { //如果oldCap大于哈希数组定义的最大容量,则将其修改成int型的最大容量 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } //标记##,扩容两倍,并且扩容之后的长度要小于默认容量最大值,oldCap要大于默认容量最小值 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } //如果前面没进行,到了此处,说明了一点,因为oldCap<=0,且oldThr>0,所以该哈希数组已经初始化过了,只是其中没有元素而已 else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; //最后呢,此处就表示首次初始化了 else { // zero initial threshold signifies using defaults //初始化默认容量大小16 newCap = DEFAULT_INITIAL_CAPACITY; //初始化临界值, 临界值 = 容量 * 加载因子 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } //此处对上面标记##的补充,就是当oldCap>0时,oldCap扩大两倍不在默认的容量范围内(16 < x < 230) if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; //把以前的哈希数组放到新的哈希数组中 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
5、删除
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; }
final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { //tab 哈希数组,p 数组下标的结点,n 数组的长度,index 当前数组的下标 Node<K,V>[] tab; Node<K,V> p; int n, index; //哈希数组不为null,长度大于0,且该链表不为null if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { //node 存储要删除的结点,e 临时变量,k 当前结点的key值,v 当前结点的value值 Node<K,V> node = null, e; K k; V v; //首结点正好是需要找的结点 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null) { //如果是红黑树中的结点,则去红黑树中找 if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { //遍历该链表,寻找其结点 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } //找到要删除的结点后,判断!matchValue,我们正常remove删除,!matchValue都是true if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { //如果是红黑树中的结点,则去红黑树中删除 if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); //此处表示要删除的结点时头结点 else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; //长度减一 --size; afterNodeRemoval(node); return node; } } //如果返回的时null,表示没有该结点,删除失败 return null; }
6、查找
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
final Node<K,V> getNode(int hash, Object key) { //tab 哈希数组,first 表示某一个数组下标的头结点,n 数组的长度,k 表示当前结点的key值 Node<K,V>[] tab; Node<K,V> first, e; int n; K k; //哈希数组不为null,长度大于0,并且该链表不为null if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //如果是头结点,则直接返回 if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; //不是头结点 if ((e = first.next) != null) { //判断是否为红黑树中的结点 if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); //遍历链表查询 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } //返回null,表示不存在该结点 return null; }