HashMap
HashMap是一个用于存储K-V的集合。每个键值对也叫做Entry对象。
HashMap是一个table数组,数组的每一个Entry元素都是链表的头结点。
HashMap的链表中,每个Entry元素通过new指针指向下一个Entry元素。
- 两个hashcode相同时,它们所对应的bucket相同,这个时候HashMap会使用链表或者红黑树来进行存储。
- 调用get()方法,当hashcode相同的时候,HashMap会使用键对象的hashcode找到bucket位置,然后调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。
- 当HashMap的大小超过了负载因子(load factor)定义的容量,会创建原来HashMap大小的两倍的bucket数组,重新调整HashMap。将原来的对象放入新的bucket数组中,也就是rehash。
- 在多线程情况下,HashMap进行扩容,链表的顺序会掉换。多线程的情况下如果条件竞争发生了,那么就死循环了。在多线程的情况下,可以使用线程安全的ConcurrentHashMap。
- 键使用String或者Integer等wrapper类,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。
HashMap源码解析
-
put方法解析
/** * 返回指定键映射的值 * @param key * @return */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * 实现Map.put和相关方法。 * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K, V>[] tab; Node<K, V> p; int n, i; //该判断判定是否需要调用resize() 初始化map if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //如果在Node数组的角标 (n - 1) & hash 中是空则直接添加 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //否则需要再该角标下进行尾插 else { Node<K, V> e; K k; //如果key相同则覆盖之前的数据 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 { //binCount该参数来判断是否将链表转为红黑树 for (int binCount = 0; ; ++binCount) { //在该角标下进行尾插 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //如果链表长度大于等于8并且Node容量大于等于64的时候转为红黑树 //在treeifyBin有对数组的判断,如果不大于64,则直接扩容。 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; } } //返回value值 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; //如果size大于12,则进行扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } /** * 初始化或者扩容 * 如果为空,则进行初始化,否则我们使用左移一位来进行扩容 * 扩容后会将所有所有元素移动到新的table中。 * * @return the table */ final Node<K, V>[] resize() { Node<K, V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { //如果已经达到最大范围,则将 threshold 也调整到最大 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } //如果不是最大,则左移一位改变其值 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } //在扩容范围值newThr等于0的时候需要给它赋值 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; } /** * 替换给定哈希的索引处bin中的所有链接节点,除非表太小,在这种情况下会调整大小。 */ final void treeifyBin(Node<K, V>[] tab, int hash) { int n, index; Node<K, V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); else if ((e = tab[index = (n - 1) & hash]) != null) { TreeNode<K, V> hd = null, tl = null; do { TreeNode<K, V> p = replacementTreeNode(e, null); if (tl == null) hd = p; else { p.prev = tl; tl.next = p; } tl = p; } while ((e = e.next) != null); if ((tab[index] = hd) != null) hd.treeify(tab); } }
-
get方法解析
/** * 返回指定键映射的值 * @param key * @return */ public V get(Object key) { Node<K, V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * 根据key获取value * * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node<K, V> getNode(int hash, Object key) { Node<K, V>[] tab; Node<K, V> first, e; int n; K k; //首先判断table是否为空,根绝key计算出的hash值的角标是否为空 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //判断该key在Node数组角标上是否为第一个元素,第一个 if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) { //如果是第一个直接返回 return first; } //如果不是则可定在链表或者TreeNode中 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); } } return null; }
-
remove方法解析
/** * 如果存在映射,从此映射中删除指定键的映射。 * * @param key key whose mapping is to be removed from the map * @return the previous value associated with {@code key}, or * {@code null} if there was no mapping for {@code key}. * (A {@code null} return can also indicate that the map * previously associated {@code null} with {@code key}.) */ public V remove(Object key) { Node<K, V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * 实现Map.remove和相关方法。 * * @param hash hash for key * @param key the key * @param value the value to match if matchValue, else ignored * @param matchValue if true only remove if value is equal * @param movable if false do not move other nodes while removing * @return the node, or null if none */ final Node<K, V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K, V>[] tab; Node<K, V> p; int n, index; //如果table不等于空,并且根据key得到的值也不为空开始进行remove操作 if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { Node<K, V> node = null, e; K k; V v; //如果角标对应的链表第一个元素就是目标remove的值,则赋值给node 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); } } //得到node之后 if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { //如果是红黑树则调用红黑树方法移除KV if (node instanceof TreeNode) ((TreeNode<K, V>) node).removeTreeNode(this, tab, movable); //把将要移除的元素的下一个节点移动到当前位置,当前位置的元素即被清除 else if (node == p) tab[index] = node.next; //如果Node数组的第一个元素,则直接根据node.next赋值为空,即移除 else p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }