java HashMap源码分析(JDK1.8)

HashMap继承AbstractMap<K,V> ,implements Map<K,V>, Cloneable, Serializable

键值对存储,HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null.

源码中可以看到一个静态的内部类(单向链表):static class Node<K,V> implements Map.Entry<K,V>

其实现Map.Entry<K,V>接口

也就是HashMap实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。

《java HashMap源码分析(JDK1.8)》

 

HashMap进行put操作的时候 :

1.先判断table(Node<K,V>[]数组)是否为空或者长度为0 如果为空则通过resize()进行扩容长度为16,负载因子为0.75

2.根据key,计算hash得出数组下标i,进行检查,该下表是否为空,如果没有值则直接插入进去

1)如果有值则说明hash冲突,如果table[i]的首个元素 是否和key一样,(通过hashCode和equals判断)相同,则将新的值覆盖旧的值。

2)如果key不相等,则判断是否是红黑树类型,如果是红黑树,则交给红黑树追加此元素。

3)如果key既不相等,也不是红黑树,则是链表,那么就遍历链表中的每一个key和给定的key是否相等。如果,链表的长度大于等于8了,则通过循环将链表改为红黑树,jdk1.8的新特性。

4.如果这三个判断返回的 e 不为null,则说明key重复,则更新key对应的value的值。

5.迭代器的modCount 变量加一。

6.最后判断,如果当前数组的长度已经大于阀值了。则重新hash。

流程图来自:https://blog.csdn.net/visant/article/details/80045154

《java HashMap源码分析(JDK1.8)》

 

下面为部分源码分析 主要是put操作分析

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

    private static final long serialVersionUID = 362498820763181265L;

    //默认初始容量16,必须为2的幂
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    //最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;

   //默认加载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    
    static final int TREEIFY_THRESHOLD = 8;

    static final int UNTREEIFY_THRESHOLD = 6;

  
    static final int MIN_TREEIFY_CAPACITY = 64;

  
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;  //存储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;
        }

   .............

        //返回此映射项的哈希值:key值的哈希码与value值的哈希码按位异或的结果
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
        //用指定值替换对应于此项的值,并返回旧值
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
         
        //比较指定对象与此项的相等性
        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;
        }
    }

     /*因为没有完美的哈希算法可以彻底避免碰撞,所以只能尽可能减少碰撞,在各方面权衡之后得到一个折 
    中方案*/
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

 

    //table是一个Node<K,V>[]数组类型,而Node<K,V>实际上就是一个元素值为<key,value>对的单向链 
    //哈希表的"key-value键值对"都是存储在Node<K,V>数组中的。 
    transient Node<K,V>[] table;

     //用来指向entrySet()返回的set集合
    transient Set<Map.Entry<K,V>> entrySet;

    //HashMap的大小,即保存的键值对的数量
    transient int size;

    //用来实现fail-fast机制的,记录HashMap结构化修改的次数
    transient int modCount;

    //下次需扩容的临界值,size>=threshold就会扩容
    //如果table数组没有被分配,则该值为初始容量值16;或若该值为0,也表明该值为初始容量值
    int threshold;

    //加载因子
    final float loadFactor;
   
 
    
    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;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            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) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            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;
    }



}

 

    原文作者:想出走的万万
    原文地址: https://blog.csdn.net/ly610501737/article/details/88659148
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞