HashMap类的JDK实现剖析

    xiaoxiao2026-01-10  9

    1.结构

    Map<K,V>是一个接口,这个接口内部还有一个接口,叫Entry<K,V>。HashMap中Entry接口的实现类是内部静态类:HashMap.Node,结构见下:

    static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; /**/ } HashMap类的结构见下:

    public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { transient Node<K,V>[] table; transient Set<Map.Entry<K,V>> entrySet; transient int size; transient int modCount; int threshold; static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 final float loadFactor; static final int TREEIFY_THRESHOLD = 8; /**/ }

    size:map中元素的个数。

    modCount:表示读以外的操作次数。用来检查并抛出ConcurrentModificationException。这是fast-fail思想。

    TREEIFY_THRESHOLD: 若某个链表的长度大于此阀值,则将node转为TreeNode,即将链表转为红黑树。注意其他链表不受影响。

    loadFactor:装填因子,与capacity搭配使用。

    DEFAULT_INITIAL_CAPACITY:初始容量。capacity为数组(桶)的长度。capacity与loadfactor决定了扩容的threshold。

    threshold: 初始=capacity*loadFactor。当size>threshold后,扩容,capacity与threshold都翻一倍。

    2.写操作

    V java.util.HashMap.put(K key, V value) 向map中添加元素,它会调用下面putVal(...)方法。 V java.util.HashMap.putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 参数onlyIfAbsent:在为true的情况下,如果map中已经有这个key了,那么此次添加操作无效。 此函数的具体操作为:首先找到key所在的链表,table[i],这个下标i=(tab.length-1) & hash。如果table[i]=null,则新建node;否则遍历链表。若在遍历过程中遇到相等的key,按照onlyIfAbsent参数行事;若遇不到,在尾部新建node。

    3.读操作

    V java.util.HashMap.get(Object key) 根据key拿到value。它会调用getNode()方法,然后返回node的value。见下。 Node<K, V> java.util.HashMap.getNode(int hash, Object key) 先根据hashcode找到所在的链表。Node<K,V> first=table[(table.length-1) & hash]。然后遍历。

    4.resize()扩容

    Node<K, V>[] java.util.HashMap.resize() 扩容一倍。

    首先申请新的数组,Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]。然后遍历旧数组的链表,分拆到新数组中。

    5.treeify

    JDK 1.8之前,HashMap是数组(桶)+链表实现,而1.8中作了大的变动,改为数组(桶)+链表+红黑树实现。 当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。
    最新回复(0)