【HashMap源码解读】(注释版本,超级详细)

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 【HashMap源码解读】(注释版本,超级详细),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文


1. 属性

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {

    /**
     * 序列号
     */
    private static final long serialVersionUID = 362498820763181265L;

    /**
     * HashMap数组结构的默认容量,容量必须为2的整数次幂,此处为16
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

    /**
     * HashMap数组的最大容量,其值必须为小于等于2的30次幂且为2的非负整数次幂
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * HashMap默认的装载因子
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * HashMap链表树化阈值,当链表长度大于等于该值时会转化为红黑树
     */
    static final int TREEIFY_THRESHOLD = 8;

    /**
     * HashMap红黑树链化的阈值,当红黑树的结点数小于等于该值时会转化为链表
     */
    static final int UNTREEIFY_THRESHOLD = 6;

    /**
     * HashMap链表树化的最小数组容量,其值至少为4*TREEIFY_THRESHOLD
     * 在将链表转化为红黑树之前,会先判断数组的长度,如果数组长度小于64,
     * 那么会选择扩容数组,而不是转化为红黑树
     */
    static final int MIN_TREEIFY_CAPACITY = 64;

    /**
     * HashMap的数组结构,其大小必须为2的整数次幂
     */
    transient Node<K, V>[] table;

    /**
     * 存放所有的HashMap元素(Entry)
     */
    transient Set<Map.Entry<K, V>> entrySet;

    /**
     * HashMap的大小,即存放元素(Node)的数量
     */
    transient int size;

    /**
     * 每次扩容和更改Map结构的计数器
     */
    transient int modCount;

    /**
     * 临界值(数组容量*装载因子),当数组实际使用大小超过该值时会进行扩容
     */
    int threshold;

    /**
     * HashMap的装载因子
     */
    final float loadFactor;
}

2. Node结点类

/**
 * HashMap链表结点,实现Entry接口
 */
static class Node<K, V> implements Map.Entry<K, V> {
    final int hash;  //hash值
    final K key; //键
    V value;  //值
    HashMap.Node<K, V> next;  //下一个结点指针

    Node(int hash, K key, V value, HashMap.Node<K, V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

    public final String toString() {
        return key + "=" + value;
    }

    public final int hashCode() {
        //结点的hashCode由key的hashCode按位异或value的hashCode计算得出
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

    /**
     * 设置结点的value,并返回旧的value
     * @param newValue 新值
     * @return 旧值
     */
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    /**
     * 两个结点的键与值均相同时判定相同
     * @param o 待比较的对象
     * @return 键值是否相同
     */
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
            if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

3. Hash静态方法

/**
 * HashMap扰动函数,减少hash碰撞
 */
static final int hash(Object key) {
    int h;
    //^:按位异或运算,同为0,异为1
    //>>>:无符号右移,忽略符号位,空位以0补齐
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

/**
 * 如果对象x所属的类实现了Comparable接口,返回x所属的类,否则返回null
 */
static Class<?> comparableClassFor(Object x) {
    if (x instanceof Comparable) {
        Class<?> c;
        Type[] ts, as;
        Type t;
        ParameterizedType p;
        if ((c = x.getClass()) == String.class) // bypass checks
            return c;
        if ((ts = c.getGenericInterfaces()) != null) {
            for (int i = 0; i < ts.length; ++i) {
                if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType) t).getRawType() ==
                                Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                    return c;
            }
        }
    }
    return null;
}

/**
 * 如果x为null或者x所属的类不为kc,返回0
 * 如果x所属的类为kc,返回k.compareTo(x)的结果
 */
@SuppressWarnings({"rawtypes", "unchecked"})
static int compareComparables(Class<?> kc, Object k, Object x) {
    return (x == null || x.getClass() != kc ? 0 :
            ((Comparable) k).compareTo(x));
}

/**
 * 返回第一个大于等于cap并且为2的非负整数幂的数
 */
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. HashMap构造方法

/**
 * @param initialCapacity 数组容量
 * @param loadFactor      装载因子
 * @throws IllegalArgumentException 
 */
public HashMap(int initialCapacity, float loadFactor) {
    //检查initialCapacity和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;
    //此处返回大于等于initialCapacity且为2的非负整数幂的数
    this.threshold = tableSizeFor(initialCapacity);
}

/**
 * @param initialCapacity 数组容量
 * 装载因子为默认装载因子
 * @throws IllegalArgumentException 
 */
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

/**
 * 默认构造方法
 * 数组容量为默认容量16,装载因子为默认装载因子
 */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

/**
 * 使用另一个Map初始化的构造函数
 */
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

5. put方法

/**
 * 调用putVal方法
 */
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

/**
 * @param hash         key的hash
 * @param key          key
 * @param value        value
 * @param onlyIfAbsent 如果为true,不更新已存在的值
 * @param evict        初始化Map时为false,已经初始化之后为true
 * @return 旧值或不存在时为null
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K, V>[] tab;   //数组
    Node<K, V> p;   //开始指向头结点
    int n, i;   //n为数组长度,i为数组下标

    //数组为空或者数组长度为0,对数组进行扩容(初始化)
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;

    //i = (n-1) & hash一定小于等于n-1,所以i为hash对应的数组下标
    //tab[i]没有元素,直接插入结点到tab[i]
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K, V> e;   //记录需插入的结点的位置
        K k;
        //key与头结点key相同,前一个条件判断key为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) {
                //在链表末尾插入结点,此时e为null
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //结点数量达到阈值,转化为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);
                    break;
                }
                //链表的结点的key与插入的结点的key相等
                if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        //已经存在相同的key
        if (e != null) {
            V oldValue = e.value;//记录key对应的旧值
            //onlyIfAbsent为false或者旧值为null,value更新为新值
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            //返回旧值
            return oldValue;
        }
    }

    //不存在相同的key,新插入元素才执行下面的代码
    ++modCount;
    //实际大小大于临界值,扩容
    if (++size > threshold)
        resize();

    afterNodeInsertion(evict);
    return null;    //返回旧值为null
}

/**
 * @param m 需put的map
 * @throws NullPointerException HashMap为空时
 */
public void putAll(Map<? extends K, ? extends V> m) {
    putMapEntries(m, true);
}

7. get方法

/**
 * 调用getNode方法
 */
public V get(Object key) {
    Node<K, V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

/**
 * @param hash key的hash
 * @param key  key
 * @return 存在时返回对应的Node,否则返回null
 */
final Node<K, V> getNode(int hash, Object key) {
    Node<K, V>[] tab;
    Node<K, V> first, e;
    int n;
    K k;
    //数组不为null且数组长度大于0,并且key对应下标的链表(或红黑树)不为null
    if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
        //先判断第一个结点是否是要get的结点
        if (first.hash == hash &&
                ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            //key对应下标处存放的是红黑树
            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;
}

8. 扩容方法

/**
 * 初始化或扩容数组
 * @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;

    //原数组容量大于0,即数组已经被初始化
    if (oldCap > 0) {
        //容量已经达到数组最大容量,不扩容
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        //原数组容量的2倍小于最大容量且原数组大于等于默认容量
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; //临界值也扩大为原临界值的2倍
        //另一种省略的情况,原数组的2倍大于等于最大容量,这时是允许的
        //只有在当前容量大于等于最大容量时才不允许扩容
    }
    //初始化容量放在原临界值中
    else if (oldThr > 0)
        newCap = oldThr;  //新的容量为原临界值
    // 原临界值为0,即使用的是HashMap()构造方法
    else {
        newCap = DEFAULT_INITIAL_CAPACITY;  //容量设为默认容量
        newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }

    //重新计算临界值
    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 {
                    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);
                    //将两条链表分别加到数组j和j+oldCap位置
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

9. remove方法

/**
 * 按照key移除HashMap中的元素
 * @param key key
 * 调用removeNode方法
 * @return 旧值或不存在时为null
 */
public V remove(Object key) {
    Node<K, V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
}

/**
 * @param hash       key的hash
 * @param key        key
 * @param value      value
 * @param matchValue 为true时只有当value相同才删除
 * @param movable    为false时在删除时不删除其他结点
 * @return key对应的Node或如果不存在为null
 */
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;
    //key索引处有元素
    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;
        //判断头结点是否是要删除的元素
        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);
            }
        }
        //存在键为key的结点并且不要求value相同或值与value相同
        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;
        }
    }
    return null;
}

/**
 * 删除所有结点
 */
public void clear() {
    Node<K, V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

10. 其他方法

/**
 * @param m     map
 * @param evict 初始化Map时为false,已经初始化之后为true
 */
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        //数组没有初始化
        if (table == null) {
            //计算数组容量
            float ft = ((float) s / loadFactor) + 1.0F;
            int t = ((ft < (float) MAXIMUM_CAPACITY) ?
                    (int) ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                threshold = tableSizeFor(t); 
        } 
        //数组还未初始化,m的容量大于临界值,触发扩容
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131181.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!