• JDK源码阅读--HashMap


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


    基于hash表的Map集合,允许key=nullvalue=nullHashMap是不同步的,不能保证Map集合的顺序,它是无序的Map集合.
    HashMap有两个参数影响它的性能: 初始化容量(initial capacity) 和 负载因子(load factor)。
    初始化容量:就是创建 hash表时的容量
    负载因子:负载因子是衡量在哈希表的容量被自动增加之前,哈希表被允许获得多少满的度量。
    当哈希表中的条目数超过负载因子和当前容量的乘积时,哈希表将被重新哈希(即重新构建内部数据结构)
    这样哈希表的桶数大约是桶数的两倍。

    默认的负载因子是0.75f,这是一个在时间和空间上的一个折中;较高的值减少了空间开销,但增加了查找成本(主要表现在HaspMapgetput操作)
       如果初始容量大于最大条目数除以负载因子,则不会发生任何重哈希操作。
    底层数据结构是链表数组,
     1 /**
     2      * 默认初始化容量是16
     3      */
     4     static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     5 
     6     /**
     7      * 最大的容量,如果较高的值由带参数的任何构造函数隐式指定,则使用。
     8      * 必须是2的幂,最大容量为1073741824
     9      */
    10     static final int MAXIMUM_CAPACITY = 1 << 30;//1073741824
    11 
    12     /**
    13      * 当构造函数中没有指定时使用的负载因子,默认是0.75f;
    14      */
    15     static final float DEFAULT_LOAD_FACTOR = 0.75f;
    16 
    17     // 一个桶的树化阈值
    18     // 当桶中元素个数超过这个值时,需要使用红黑树节点替换链表节点
    19     // 这个值必须为 8,要不然频繁转换效率也不高
    20     static final int TREEIFY_THRESHOLD = 8;
    21 
    22     // 一个树的链表还原阈值
    23     // 当扩容时,桶中元素个数小于这个值,就会把树形的桶元素 还原(切分)为链表结构
    24     // 这个值应该比上面那个小,至少为 6,避免频繁转换
    25     static final int UNTREEIFY_THRESHOLD = 6;
    26 
    27 
    28     // 哈希表的最小树形化容量
    29     // 当哈希表中的容量大于这个值时,表中的桶才能进行树形化
    30     // 否则桶内元素太多时会扩容,而不是树形化
    31     // 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
    32     static final int MIN_TREEIFY_CAPACITY = 64;
    
    
    

    基本的hash表节点:

     1     /**
     2      * 基本的hash表节点,
     3      * (参见下面的forTreeNode子类,以及LinkedHashMap中的条目子类。)
     4      */
     5     static class Node<K,V> implements Map.Entry<K,V> {
     6         final int hash;//hash值 final修饰的
     7         final K key;//键  final修饰的
     8         V value;//
     9         Node<K,V> next;//后置节点
    10 
    11         //构造函数
    12         Node(int hash, K key, V value, Node<K,V> next) {
    13             this.hash = hash;
    14             this.key = key;
    15             this.value = value;
    16             this.next = next;
    17         }
    18 
    19         public final K getKey()        { return key; }
    20         public final V getValue()      { return value; }
    21         public final String toString() { return key + "=" + value; }
    22 
    23         public final int hashCode() {
    24             return Objects.hashCode(key) ^ Objects.hashCode(value);
    25         }
    26 
    27         public final V setValue(V newValue) {
    28             V oldValue = value;
    29             value = newValue;
    30             return oldValue;
    31         }
    32 
    33         public final boolean equals(Object o) {
    34             if (o == this)
    35                 return true;
    36             if (o instanceof Map.Entry) {
    37                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
    38                 if (Objects.equals(key, e.getKey()) &&
    39                     Objects.equals(value, e.getValue()))
    40                     return true;
    41             }
    42             return false;
    43         }
    44     }
     1 /**
     2      * The table, initialized on first use, and resized as
     3      * necessary. When allocated, length is always a power of two.
     4      * (We also tolerate length zero in some operations to allow
     5      * bootstrapping mechanics that are currently not needed.)
     6      * 第一次初始化的时候使用这个表。当分配时,长度总是2的幂
     7      */
     8     transient Node<K,V>[] table;
     9 
    10     /**
    11      * Holds cached entrySet(). Note that AbstractMap fields are used for keySet() and values().
    12      * 保存缓存entrySet()。注意AbstractMap字段用于keySet()和values()。
    13      */
    14     transient Set<Map.Entry<K,V>> entrySet;
    15 
    16     /**
    17      * The number of key-value mappings contained in this map.
    18      * 此映射中包含的键值映射的数目。
    19      */
    20     transient int size;
    21 
    22     /**
    23      * 被修改的次数
    24      */
    25     transient int modCount;
    26 
    27 
    28 
    29     /**
    30      * The next size value at which to resize (capacity * load factor).
    31      * 要调整大小的下一个大小值(容量*负载因子)。
    32      * @serial
    33      */
    34     int threshold;//要调整大小的下一个大小值(容量*负载因子)。
    35 
    36     /**
    37      * 哈希表的负载因子。
    38      * @serial
    39      */
    40     final float loadFactor;

    构造函数:

     1 /**
     2      * 构造一个空的HashMap,使用专门的初始化容量和默认的负载因子。
     3      * @param  initialCapacity the initial capacity.
     4      * @throws IllegalArgumentException if the initial capacity is negative.
     5      */
     6     public HashMap(int initialCapacity) {
     7         this(initialCapacity, DEFAULT_LOAD_FACTOR);
     8     }
     9 
    10     /**
    11      * (16) and the default load factor (0.75).
    12      * 构造一个空的HashMap,使用默认的初始化容量(16)和默认的负载因子(0.75f)。
    13      */
    14     public HashMap() {
    15         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    16     }
    17 
    18     /**
    19      * 构造一个新的HashMap,使用与指定映射相同的映射。装载因子使用默认的(0.75f),和足够容纳指定Map中的映射的初始容量。
    20      * @param   m the map whose mappings are to be placed in this map 要在此映射中放置其映射的映射的映射
    21      * @throws  NullPointerException if the specified map is null 如果这个指定的map为null,则抛出空指针异常NullPointerException
    22      */
    23     public HashMap(Map<? extends K, ? extends V> m) {
    24         this.loadFactor = DEFAULT_LOAD_FACTOR;
    25         putMapEntries(m, false);
    26     }
     1  /**
     2      * 实现 Map.putAll和 Map的构造函数
     3      * @param m the map
     4      * @param evict  当最初构造这个map的时候为false,否则为true(传递到之后的插入节点的方法中)。
     5      */
     6     final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
     7         int s = m.size();
     8         if (s > 0) {
     9             if (table == null) { // pre-size
    10                 float ft = ((float)s / loadFactor) + 1.0F;
    11                 int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY);
    12                 if (t > threshold) {
    13                     threshold = tableSizeFor(t);
    14                 }
    15             }else if (s > threshold) {
    16                 resize();
    17             }
    18             for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
    19                 K key = e.getKey();
    20                 V value = e.getValue();
    21                 putVal(hash(key), key, value, false, evict);
    22             }
    23         }
    24     }
     1 /**
     2      * @return 返回此映射中键值映射的数目。
     3      */
     4     public int size() {
     5         return size;
     6     }
     7 
     8     /**
     9      *  判断该map是否为空
    10      * @return  当此map中不含键值对的时候,返回true.
    11      */
    12     public boolean isEmpty() {
    13         return size == 0;
    14     }

    get方法(先比较hash,若相等在比较equals):

    1. Key==null的时候,判断map也为空,就返回null
    2. 根据node数组下标找到node数组下标
    3. 如果当前node链表只存在一个数据就直接取value值
    如果当前node链表存在多个node元素,则循环遍历node链表,分别对他们的hash值和key值,value值进行判断,知道找到node以后返回Value值,如果没有找到返回null。

     1  /**
     2      * 根据键key获取值value,如果此map不包含这个key,则返回null。
     3      * Returns the value to which the specified key is mapped,
     4      * or {@code null} if this map contains no mapping for the key.
     5      *
     6      * <p>More formally, if this map contains a mapping from a key
     7      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     8      * key.equals(k))}, then this method returns {@code v}; otherwise
     9      * it returns {@code null}.  (There can be at most one such mapping.)
    10      *
    11      * <p>A return value of {@code null} does not <i>necessarily</i>
    12      * indicate that the map contains no mapping for the key; it's also
    13      * possible that the map explicitly maps the key to {@code null}.
    14      * The {@link #containsKey containsKey} operation may be used to
    15      * distinguish these two cases.
    16      *
    17      * @see #put(Object, Object)
    18      */
    19     public V get(Object key) {
    20         Node<K,V> e;
    21         return (e = getNode(hash(key), key)) == null ? null : e.value;
    22     }
    23 
    24     /**
    25      * Implements Map.get and related methods
    26      * Map.get相关方法的实现
    27      * @param hash hash for key  键key的hash值
    28      * @param key the key        键key
    29      * @return the node, or null if none  返回根据key及key的hash找到的这个节点;如果这个节点为空,则返回null
    30      *
    31      *
    32      */
    33     final Node<K,V> getNode(int hash, Object key) {
    34         Node<K,V>[] tab;
    35         Node<K,V> first, e;
    36         int n;
    37         K k;
    38         if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
    39             // always check first node 总是会检查哈希表的第一个节点
    40             if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) {
    41                 return first;
    42             }
    43             if ((e = first.next) != null) {//如果第一个节点的下一个节点不为空
    44                 if (first instanceof TreeNode) {//如果该节点是TreeNode类型的(红黑树)
    45                     return ((TreeNode<K, V>) first).getTreeNode(hash, key);//调用红黑树的查找方法
    46                 }
    47                 do {
    48                     if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
    49                         return e;
    50                     }
    51                 } while ((e = e.next) != null);
    52             }
    53         }
    54         return null;
    55     }

    put方法:

    1. 首先判断node数组,也就是数组是否为空,为空的话就初始化hashmap
    2. 如果node数组不为空的话,就判断key是否为空,为空的话就将放到数组的第一个位置
    3. 就通过key获取hash值,通过hash值做比较,如果key的hash值相等 并且key.equals(e.key)也相等的话,就将新的value替换掉旧的。如果条件不满足就创建一个node,且用上一个node的next指向新创建的node 。

     1 /**
     2      * 往map中添加新的键值对,如果键key存在,则将该键key对应的旧值value替换为新值value
     3      *
     4      * @param key 要与指定值关联的键
     5      * @param value 值与指定的键关联
     6      * @return 与key关联的前一个值,如果没有key的映射,则为null。(null返回值还可以指示以前将null与key关联的映射。)
     7      */
     8     public V put(K key, V value) {
     9         return putVal(hash(key), key, value, false, true);
    10     }
    11 
    12     /**
    13      * Implements Map.put and related methods
    14      * 实现Map.put和相关方法
    15      * @param hash hash for key    key的hash值
    16      * @param key the key           键
    17      * @param value the value to put  值
    18      * @param onlyIfAbsent   如果是true,不能改变已存在的值
    19      * @param evict  如果为false,该表处于创建模式
    20      * @return  返回前一个值,如果没有,则为空
    21      * 根据key算hash,根据容量和hash算index,table[index]没有直接添加到数组中,table[index]有,若index位置同一个key则更新,
    22      *      否则遍历next是否有,有则更新,无则新增,最后根据thread与size判断是否扩容。注:扩容时容量翻倍,重新算hash复制到新数组
    23      *
    24      */
    25     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    26                    boolean evict) {
    27         Node<K,V>[] tab;
    28         Node<K,V> p;
    29         int n, i;
    30         if ((tab = table) == null || (n = tab.length) == 0) {
    31             n = (tab = resize()).length;
    32         }
    33         if ((p = tab[i = (n - 1) & hash]) == null) {
    34             tab[i] = newNode(hash, key, value, null);
    35         }else {
    36             Node<K,V> e; K k;
    37             if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) {
    38                 e = p;
    39             }else if (p instanceof TreeNode) {
    40                 e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
    41             }
    42             else {
    43                 for (int binCount = 0; ; ++binCount) {
    44                     if ((e = p.next) == null) {
    45                         p.next = newNode(hash, key, value, null);
    46                         if (binCount >= TREEIFY_THRESHOLD - 1) { // -1 for 1st
    47                             treeifyBin(tab, hash);
    48                         }
    49                         break;
    50                     }
    51                     if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
    52                         break;
    53                     }
    54                     p = e;
    55                 }
    56             }
    57             if (e != null) { // existing mapping for key
    58                 V oldValue = e.value;
    59                 if (!onlyIfAbsent || oldValue == null) {
    60                     e.value = value;
    61                 }
    62                 afterNodeAccess(e);
    63                 return oldValue;
    64             }
    65         }
    66         ++modCount;
    67         if (++size > threshold) {
    68             resize();
    69         }
    70         afterNodeInsertion(evict);
    71         return null;
    72     }
  • 相关阅读:
    体验cygwin纪实
    播布客视频PIT专用播放器MBOO2015
    rpm基本命令参考
    rhel7.x配置本地yum
    mtr网络连通性测试
    Oracle下载汇聚
    Spring Cloud心跳监测
    Hystrix的用法
    Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
    dubbo异步调用三种方式
  • 原文地址:https://www.cnblogs.com/lixianyuan-org/p/10530263.html
Copyright © 2020-2023  润新知