zoukankan      html  css  js  c++  java
  • hashMap 源码注释分析(一)

      1 package MyHashMap;
      2 
      3 import java.io.IOException;
      4 import java.io.InvalidObjectException;
      5 import java.io.Serializable;
      6 import java.lang.reflect.ParameterizedType;
      7 import java.lang.reflect.Type;
      8 import java.util.*;
      9 import java.util.function.BiConsumer;
     10 import java.util.function.BiFunction;
     11 import java.util.function.Consumer;
     12 import java.util.function.Function;
     13 import sun.misc.SharedSecrets;
     14 
     15 
     16 
     17 public class HashMap<K,V> extends AbstractMap<K,V>
     18     implements Map<K,V>, Cloneable, Serializable {
     19 
     20     private static final long serialVersionUID = 362498820763181265L;
     21 
     22 
     23     /**
     24      * 默认初始容量-必须是2的幂。
     25      */
     26     static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     27 
     28 
     29 
     30     /**
     31      * 最大容量。
     32      *必须是2的幂<=1<<30。
     33      */
     34     static final int MAXIMUM_CAPACITY = 1 << 30;
     35 
     36     /**
     37      * 构造函数中未指定时使用的加载因子。
     38      */
     39     static final float DEFAULT_LOAD_FACTOR = 0.75f;
     40 
     41     /**
     42      * 使用树而不是数组作为储存数据的阈(yu)值
     43      * 数组将转换为红黑树该值必须大于2,并且至少应为8,
     44      */
     45     static final int TREEIFY_THRESHOLD = 8;
     46 
     47     /**
     48      * 在哈希表扩容时,如果发现链表长度小于 6,则会由树重新退化为链表
     49      */
     50     static final int UNTREEIFY_THRESHOLD = 6;
     51 
     52     /**
     53      * 在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。
     54      * 这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化
     55      */
     56     static final int MIN_TREEIFY_CAPACITY = 64;
     57 
     58     /**
     59      * 当数组节点超过 TREEIFY_THRESHOLD 阈值时使用(有关TreeNode子类的信息,请参见下文,有关Entry子类的信息,请参见LinkedHashMap。)
     60      *
     61      * 该类为静态内部类,hashMap使用静态内部类创建链表
     62      */
     63     static class Node<K,V> implements Map.Entry<K,V> {
     64         final int hash;
     65         final K key;
     66         V value;
     67         Node<K,V> next;
     68 
     69         Node(int hash, K key, V value, Node<K,V> next) {
     70             this.hash = hash;
     71             this.key = key;
     72             this.value = value;
     73             this.next = next;
     74         }
     75 
     76         public final K getKey()        { return key; }
     77         public final V getValue()      { return value; }
     78         public final String toString() { return key + "=" + value; }
     79 
     80         public final int hashCode() {
     81             return Objects.hashCode(key) ^ Objects.hashCode(value);
     82         }
     83 
     84         public final V setValue(V newValue) {
     85             V oldValue = value;
     86             value = newValue;
     87             return oldValue;
     88         }
     89 
     90         public final boolean equals(Object o) {
     91             if (o == this)
     92                 return true;
     93             if (o instanceof Map.Entry) {
     94                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
     95                 if (Objects.equals(key, e.getKey()) &&
     96                     Objects.equals(value, e.getValue()))
     97                     return true;
     98             }
     99             return false;
    100         }
    101     }
    102 
    103     /* ---------------- Static utilities -------------- */
    104 
    105     /**
    106      * 计算key的hash值
    107      * >>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;
    108      * >>>表示无符号右移,也叫逻辑右移,即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。
    109      * 使用key的hash值与该hash值右移16位异或
    110      * 异或的定义,两个数的二进制表示进行按位异或,相同为0 , 相异为1,
    111      */
    112     static final int hash(Object key) {
    113         int h;
    114         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    115     }
    116 
    117     /**
    118      * 如果x的类型实现Comparable<T> 返回x的 Class<?>
    119      * 如果不是返回 null
    120      */
    121     static Class<?> comparableClassFor(Object x) {
    122         if (x instanceof Comparable) {
    123             Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
    124             if ((c = x.getClass()) == String.class) // 将x.getClass() 赋值给c
    125                 return c;
    126             if ((ts = c.getGenericInterfaces()) != null) {
    127                 for (int i = 0; i < ts.length; ++i) {
    128                     if (((t = ts[i]) instanceof ParameterizedType) &&
    129                         ((p = (ParameterizedType)t).getRawType() ==
    130                          Comparable.class) &&
    131                         (as = p.getActualTypeArguments()) != null &&
    132                         as.length == 1 && as[0] == c) // type arg is c
    133                         return c;
    134                 }
    135             }
    136         }
    137         return null;
    138     }
    139 
    140 
    141     public static void main(String[] args) {
    142         int n = 33 - 1;
    143         System.out.println(Integer.toBinaryString(n));
    144         n |= n >>> 1;
    145         System.out.println(Integer.toBinaryString(n));
    146         n |= n >>> 2;
    147         System.out.println(Integer.toBinaryString(n));
    148         n |= n >>> 4;
    149         System.out.println(Integer.toBinaryString(n));
    150         n |= n >>> 8;
    151         System.out.println(Integer.toBinaryString(n));
    152         n |= n >>> 16;
    153         System.out.println(n);
    154     }
    155 
    156     /**
    157      * Returns k.compareTo(x) if x matches kc (k's screened comparable
    158      * class), else 0.
    159      */
    160     @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
    161     static int compareComparables(Class<?> kc, Object k, Object x) {
    162         return (x == null || x.getClass() != kc ? 0 :
    163                 ((Comparable)k).compareTo(x));
    164     }
    165 
    166     /**
    167      * 对于给定的目标容量,返回两倍大小的幂。
    168      */
    169     static final int tableSizeFor(int cap) {
    170         int n = cap - 1; //保证容量值输入为2的幂时不会被再次扩容2倍  int n = 33 - 1; 100000
    171         n |= n >>> 1;// 110000   n 与 n逻辑右移1位 赋值给n  目标为:把二进制数的所有位都变成1
    172         // 1移1位与运算变成两个1 ,两个1移两位变成4个 4个变成8个  8个变16个 16个变32 覆盖所有的数  完成扩容。完美!!!
    173         n |= n >>> 2;//111100         ....
    174         n |= n >>> 4;//111111       .....
    175         n |= n >>> 8;//111111
    176         n |= n >>> 16;//111111
    177         return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    178     }
    179 
    180     /* ---------------- Fields -------------- */
    181 
    182     /**
    183      * transient修饰符的作用是使该变量在序列化的时候不会被储存。
    184      * table是储存了容器中所有的元素(地址)
    185      * HashMap的哈希桶数组,非常重要的存储结构,用于存放表示键值对数据的Node元素。
    186      */
    187     transient Node<K,V>[] table;
    188 
    189     /**
    190      * HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能。
    191      */
    192     transient Set<Map.Entry<K,V>> entrySet;
    193 
    194     /**
    195      * HashMap中实际存在的Node数量,注意这个数量不等于table的长度,
    196      * 甚至可能大于它,因为在table的每个节点上是一个链表(或RBT)结构,可能不止有一个Node元素存在。
    197      */
    198     transient int size;
    199 
    200     /**
    201      * HashMap的数据被修改的次数,这个变量用于迭代过程中的Fail-Fast机制,
    202      * 其存在的意义在于保证发生了线程安全问题时,能及时的发现(操作前备份的count和当前modCount不相等)并抛出异常终止操作。
    203      */
    204     transient int modCount;
    205 
    206     /**
    207      * The next size value at which to resize (capacity * load factor).
    208      *HashMap的扩容阈值,在HashMap中存储的Node键值对超过这个数量时,自动扩容容量为原来的二倍。
    209      * @serial
    210      */
    211     // (The javadoc description is true upon serialization.
    212     // Additionally, if the table array has not been allocated, this
    213     // field holds the initial array capacity, or zero signifying
    214     // DEFAULT_INITIAL_CAPACITY.)
    215     int threshold;
    216 
    217     /**
    218      * HashMap的负载因子,可计算出当前table长度下的扩容阈值:threshold = loadFactor * table.length。
    219      *
    220      * @serial
    221      */
    222     final float loadFactor;
    223 
    224     /* ---------------- Public operations -------------- */
    225 
    226     /**
    227      *hashMap的构造器,
    228      * @param  initialCapacity 初始化大小容量 默认16
    229      * @param  loadFactor      负载因子 默认0.75
    230      * @throws IllegalArgumentException if the initial capacity is negative
    231      *         or the load factor is nonpositive
    232      */
    233     public HashMap(int initialCapacity, float loadFactor) {
    234         if (initialCapacity < 0)
    235             throw new IllegalArgumentException("Illegal initial capacity: " +
    236                                                initialCapacity);
    237         if (initialCapacity > MAXIMUM_CAPACITY)//如果初始容量大于最大容量 则=最大容量
    238             initialCapacity = MAXIMUM_CAPACITY;
    239         if (loadFactor <= 0 || Float.isNaN(loadFactor))
    240             throw new IllegalArgumentException("Illegal load factor: " +
    241                                                loadFactor);
    242         this.loadFactor = loadFactor;//赋值负载因子
    243         this.threshold = tableSizeFor(initialCapacity);//把容量转为范围内最大2的幂赋值给扩容阈值
    244     }
    245 
    246     /**
    247      * Constructs an empty <tt>HashMap</tt> with the specified initial
    248      * capacity and the default load factor (0.75).
    249      *
    250      * @param  initialCapacity the initial capacity.
    251      * @throws IllegalArgumentException if the initial capacity is negative.
    252      */
    253     public HashMap(int initialCapacity) {
    254         this(initialCapacity, DEFAULT_LOAD_FACTOR);
    255     }
    256 
    257     /**
    258      * Constructs an empty <tt>HashMap</tt> with the default initial capacity
    259      * (16) and the default load factor (0.75).
    260      */
    261     public HashMap() {
    262         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    263     }
    264 
    265     /**
    266      * Constructs a new <tt>HashMap</tt> with the same mappings as the
    267      * specified <tt>Map</tt>.  The <tt>HashMap</tt> is created with
    268      * default load factor (0.75) and an initial capacity sufficient to
    269      * hold the mappings in the specified <tt>Map</tt>.
    270      *
    271      * @param   m the map whose mappings are to be placed in this map
    272      * @throws  NullPointerException if the specified map is null
    273      */
    274     public HashMap(Map<? extends K, ? extends V> m) {
    275         this.loadFactor = DEFAULT_LOAD_FACTOR;
    276         putMapEntries(m, false);
    277     }
    278 
    279     /**
    280      * Implements Map.putAll and Map constructor
    281      * putALL 方法  把map添加到当前map中。
    282      *
    283      * @param m the map
    284      * @param evict false when initially constructing this map, else
    285      * true (relayed to method afterNodeInsertion).
    286      */
    287     final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    288         int s = m.size();
    289         if (s > 0) {
    290             if (table == null) { //如果map的容器是空的,没有初始化
    291                 float ft = ((float)s / loadFactor) + 1.0F; // threshold = loadFactor * table.length。计算容器需要的大小
    292                 int t = ((ft < (float)MAXIMUM_CAPACITY) ?
    293                          (int)ft : MAXIMUM_CAPACITY);
    294                 if (t > threshold)//如果容器大小大于当前的扩容阈值,则阈值扩容为范围内最大2的幂
    295                     threshold = tableSizeFor(t);
    296             }
    297             else if (s > threshold)//如果table 不为 null 且 传入的map的大小大于当前的扩容阈值
    298                 resize();//table 扩容
    299             for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {//给table赋值, 就是put操作
    300                 K key = e.getKey();
    301                 V value = e.getValue();
    302                 putVal(hash(key), key, value, false, evict);// put操作
    303             }
    304         }
    305     }

    HashMap 超详细注释解读

  • 相关阅读:
    方法参数个数最多不宜超过4个
    避免方法中使用大量局部变量
    JQuery学习备忘
    CSS学习备忘
    解析Path方法备忘
    获取差集合的一种实现思路
    前台JSP页面独立化
    requireJs的使用
    handlebar
    移动端h5<a>标签点击样式去除
  • 原文地址:https://www.cnblogs.com/hxz-nl/p/11944357.html
Copyright © 2011-2022 走看看