一、HashMap 中的成员变量  

  

    成员变量说明:

 1 ① 默认初始化容量 16(必须为2的次幂)
 2 /**
 3  * The default initial capacity - MUST be a power of two.
 4  */
 5 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
 6 
 7 ② 最大容量  1<<30 == 2^30
 8 /**
 9  * The maximum capacity, used if a higher value is implicitly specified
10  * by either of the constructors with arguments.
11  * MUST be a power of two <= 1<<30.
12  */
13 static final int MAXIMUM_CAPACITY = 1 << 30;
14 
15 ③ 默认加载因子  0.75 (在构造函数中未指定时使用的加载系数)
16 /**
17  * The load factor used when none specified in constructor.
18  */
19 static final float DEFAULT_LOAD_FACTOR = 0.75f;
20 
21 ④ 一个空的 Empty 的数组 (当表未膨胀时要共享的空表实例)
22 /**
23  * An empty table instance to share when the table is not inflated.
24  */
25 static final Entry<?,?>[] EMPTY_TABLE = {};
26 
27 ⑤ 存储数据的数组,并且 table 的长度必须是2的次幂
28 /**
29  * The table, resized as necessary. Length MUST Always be a power of two.
30  */
31 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
32 
33 ⑥ Map 中元素的个数
34 /**
35  * The number of key-value mappings contained in this map.
36  */
37 transient int size;
38 
39 ⑦ 扩容的临界值,当数组的元素达到该值时,考虑扩容(下一个要调整大小的大小值(容量负载因子)长度*负载因子)
40 /**
41  * The next size value at which to resize (capacity * load factor).
42  * @serial
43  */
44 // If table == EMPTY_TABLE then this is the initial capacity at which the
45 // table will be created when inflated.
46 int threshold;
47 
48 ⑧ 负载因子
49 /**
50  * The load factor for the hash table.
51  *
52  * @serial
53  */
54 final float loadFactor;
55 
56 ⑨ 记录对该 HashMap 进行结构修改的次数,用于快速失败(fail-fast)
57 /**
58  * The number of times this HashMap has been structurally modified
59  * Structural modifications are those that change the number of mappings in
60  * the HashMap or otherwise modify its internal structure (e.g.,
61  * rehash).  This field is used to make iterators on Collection-views of
62  * the HashMap fail-fast.  (See ConcurrentModificationException).
63  */
64 transient int modCount;
65 
66 ⑩  替代哈希阈值默认值
67 /**
68  * The default threshold of map capacity above which alternative hashing is
69  * used for String keys. Alternative hashing reduces the incidence of
70  * collisions due to weak hash code calculation for String keys.
71  * <p/>
72  * This value may be overridden by defining the system property
73  * {@code jdk.map.althashing.threshold}. A property value of {@code 1}
74  * forces alternative hashing to be used at all times whereas
75  * {@code -1} value ensures that alternative hashing is never used.
76  */
77 static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
78 
79 11  备用哈希
80 /**
81  * A randomizing value associated with this instance that is applied to
82  * hash code of keys to make hash collisions harder to find. If 0 then
83  * alternative hashing is disabled.
84  */
85 transient int hashSeed = 0;
86 
87 
88 12 对整个HashMap的映射视图
89 // Views
90 private transient Set<Map.Entry<K,V>> entrySet = null;
91 
92 13 标识该类的 序列化ID
93 private static final long serialVersionUID = 362498820763181265L;

 

二、HashMap 的构造器

    HashMap 提供了四个构造器,可以分为两类,下面进行学习:

  1、无参构造器

1 public HashMap() {
2     this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
3 }

 

    在这里使用默认容量(16)和默认加载因子(0.75)调用本类构造器。

  2、指定容量的构造器

1 public HashMap(int initialCapacity) {
2     this(initialCapacity, DEFAULT_LOAD_FACTOR);
3 }

 

    这里使用了指定的初始容量,仍然采用默认的加载因子。

  3、指定容量和负载因子

 1 public HashMap(int initialCapacity, float loadFactor) {
 2     if (initialCapacity < 0)
 3         throw new IllegalArgumentException("Illegal initial capacity: " +
 4                                            initialCapacity);
 5     if (initialCapacity > MAXIMUM_CAPACITY)
 6         initialCapacity = MAXIMUM_CAPACITY;
 7     if (loadFactor <= 0 || Float.isNaN(loadFactor))
 8         throw new IllegalArgumentException("Illegal load factor: " +
 9                                            loadFactor);
10 
11     this.loadFactor = loadFactor;
12     threshold = initialCapacity;
13     init();
14 }

 

    该方法使用传递进来的容量和加载因子进行初始化,对两个成员变量进行了赋值。

void init() {}

      并且 init() 方法中也为给数组分配初始空间,那什么时候才会给数组进行内存的分配呢?

    来看一下 put() 方法:

      

 

     当我们第一次调用 put() 方法时, table 还是空数组,就会执行 inflateTable() 方法,

 1 /**
 2  * Inflates the table.
 3  */
 4 private void inflateTable(int toSize) {
 5     // Find a power of 2 >= toSize
 6     int capacity = roundUpToPowerOf2(toSize);
 7 
 8     threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
 9     table = new Entry[capacity];
10     initHashSeedAsNeeded(capacity);
11 }
12 
13 private static int roundUpToPowerOf2(int number) {
14     // assert number >= 0 : "number must be non-negative";
15     return number >= MAXIMUM_CAPACITY
16             ? MAXIMUM_CAPACITY
17             : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
18 }

 

     在这里就会为 table 分配一个 capacity 大小的空间,并且保证了 capacity 是 2的次幂

    注意:在 JDK1.7 较低的版本中,会在构造器中直接为 table 分配内存(下面是JDK1.7.0_7版本),在高版本中(趋于JDK1.8)会在put() 方法中为 table 指定容量大小。【可以理解为JDK7 与 JDK8 的区别点】

 1 public HashMap(int initialCapacity, float loadFactor) {
 2     if (initialCapacity < 0)
 3         throw new IllegalArgumentException("Illegal initial capacity: " +
 4                                            initialCapacity);
 5     if (initialCapacity > MAXIMUM_CAPACITY)
 6         initialCapacity = MAXIMUM_CAPACITY;
 7     if (loadFactor <= 0 || Float.isNaN(loadFactor))
 8         throw new IllegalArgumentException("Illegal load factor: " +
 9                                            loadFactor);
10 
11     // Find a power of 2 >= initialCapacity
12     int capacity = 1;
13     while (capacity < initialCapacity)
14         capacity <<= 1;
15 
16     this.loadFactor = loadFactor;
17     threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
18     table = new Entry[capacity];
19     useAltHashing = sun.misc.VM.isBooted() &&
20             (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
21     init();
22 }

 

  4、传入 Map 的构造器

1 public HashMap(Map<? extends K, ? extends V> m) {
2     this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
3                   DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
4     inflateTable(threshold);
5 
6     putAllForCreate(m);
7 }

 

    可以通过传入一个 Map 来创建HashMap 集合。主要做了两件事:① 扩充数组;② 把参数Map的节点加入到 HashMap中;

 1 private void putAllForCreate(Map<? extends K, ? extends V> m) {
 2     //遍历参数的map所有的 entrySet(),并根据entrySet() 的 key-value来构建对应节点
 3     for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
 4         putForCreate(e.getKey(), e.getValue());
 5 }
 6 
 7 private void putForCreate(K key, V value) {
 8     int hash = null == key ? 0 : hash(key);   //根据 key计算 hash值
 9     int i = indexFor(hash, table.length);     //根据 key 的 hash值计算在数组中的下标位置
10 
11     /**
12      * Look for preexisting entry for key.  This will never happen for
13      * clone or deserialize.  It will only happen for construction if the
14      * input Map is a sorted map whose ordering is inconsistent w/ equals.
15      */
16      /**
17         ① 根据下标获取对应的元素e,判断是否为空
18             ② 如果 e 不为空,比较要添加元素的hash值和 e 的hash值是否一样
19             ③ 如果 e 和要添加元素的 hash 值一样,则进一步进行==比较和 equals()比较
20             ④ 如果 hash值 并且它们的 key 的值也一样,则用新值替代旧值(覆盖操作) 
21         ⑤ 直到 e 为空,执行下面的 createEntry()
22      */
23     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
24         Object k;
25         if (e.hash == hash &&
26             ((k = e.key) == key || (key != null && key.equals(k)))) {
27             e.value = value;
28             return;
29         }
30     }
31     //如果当前位置为 null,创建Entry
32     createEntry(hash, key, value, i);
33 }
34 
35 
36 void createEntry(int hash, K key, V value, int bucketIndex) {
37     //获取当前数组下标中的 元素 e
38     Entry<K,V> e = table[bucketIndex];
39     //创建一个新的元素节点,并且它的下一个元素指向 e,同时把新建节点放在数组中
40     table[bucketIndex] = new Entry<>(hash, key, value, e);
41     //元素数量加1
42     size++;
43 }

 

三、HashMap 中的节点

  JDK中存放的是 Entry 类型的节点,它继承了 Map接口中的 Entry类型,Entry 类型声明了四个成员变量,并且其中有一个为 Entry 类型的 next,为该节点所指向的下一个节点,形成了一个单向链表的数据结构。

 1 static class Entry<K,V> implements Map.Entry<K,V> {
 2     final K key;         //当前节点的 key
 3     V value;             //当前节点的 value
 4     Entry<K,V> next;     //当前节点所指向的下一个节点
 5     int hash;            //当前节点的 hash 值
 6 
 7     /**
 8      * Creates new entry.
 9      */
10     Entry(int h, K k, V v, Entry<K,V> n) {
11         value = v;
12         next = n;
13         key = k;
14         hash = h;
15     }
16 
17     public final K getKey() {
18         return key;
19     }
20 
21     public final V getValue() {
22         return value;
23     }
24 
25     public final V setValue(V newValue) {
26         V oldValue = value;
27         value = newValue;
28         return oldValue;
29     }
30 
31     public final boolean equals(Object o) {
32         if (!(o instanceof Map.Entry))
33             return false;
34         Map.Entry e = (Map.Entry)o;
35         Object k1 = getKey();
36         Object k2 = e.getKey();
37         if (k1 == k2 || (k1 != null && k1.equals(k2))) {
38             Object v1 = getValue();
39             Object v2 = e.getValue();
40             if (v1 == v2 || (v1 != null && v1.equals(v2)))
41                 return true;
42         }
43         return false;
44     }
45 
46     public final int hashCode() {
47         return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
48     }
49 
50     public final String toString() {
51         return getKey() + "=" + getValue();
52     }
53 
54     /**
55      * This method is invoked whenever the value in an entry is
56      * overwritten by an invocation of put(k,v) for a key k that's already
57      * in the HashMap.
58      */
59     void recordAccess(HashMap<K,V> m) {
60     }
61 
62     /**
63      * This method is invoked whenever the entry is
64      * removed from the table.
65      */
66     void recordRemoval(HashMap<K,V> m) {
67     }
68 }

 

四、HashMap 中 table 的分配

  HashMap 中为 table 分配空间执行 inflateTable() 方法,会根据 参数toSize 计算大于等于 toSize的最大2的整数次幂(后面解释会为什么)

 1 /**
 2  * Inflates the table.
 3  */
 4 private void inflateTable(int toSize) {
 5     // Find a power of 2 >= toSize
 6     int capacity = roundUpToPowerOf2(toSize);  //确保是 2的次幂
 7 
 8     threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
 9     table = new Entry[capacity];
10     initHashSeedAsNeeded(capacity);
11 }
12 
13 private static int roundUpToPowerOf2(int number) {
14     // assert number >= 0 : "number must be non-negative";
15     return number >= MAXIMUM_CAPACITY
16             ? MAXIMUM_CAPACITY
17             : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
18 }

 

  其中会调用 Integer 的 hightestOneBit() 方法来计算2的整数次幂:

1 public static int highestOneBit(int i) {
2     // HD, Figure 3-1
3     i |= (i >>  1);
4     i |= (i >>  2);
5     i |= (i >>  4);
6     i |= (i >>  8);
7     i |= (i >> 16);
8     return i - (i >>> 1);
9 }

 

五、hash 函数

  HashMap 中通过异或和无符号右移来尽可能的打乱 key 的 hashCode(),增加hashCode 的随机性,从而来降低 Hash碰撞,使得元素分布更加均匀。

 1 final int hash(Object k) {
 2     int h = hashSeed;   //hashSeed = 0;
 3     if (0 != h && k instanceof String) {
 4         return sun.misc.Hashing.stringHash32((String) k);
 5     }
 6 
 7     h ^= k.hashCode();
 8 
 9     // This function ensures that hashCodes that differ only by
10     // constant multiples at each bit position have a bounded
11     // number of collisions (approximately 8 at default load factor).
12     h ^= (h >>> 20) ^ (h >>> 12);
13     return h ^ (h >>> 7) ^ (h >>> 4);
14 }

 

六、计算元素在数组的下标

  在这里用了一个巧妙的算法, hash & (length - 1) == hash % length,而这一算法的前提就是保证 length 是2的整数次幂,而且&运算比 % 的效率要高。(后面有详解)

1 /**
2  * Returns index for hash code h.
3  */
4 static int indexFor(int h, int length) {
5     // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
6     return h & (length-1);
7 }

 

七、添加元素 put() 系列

  1、添加单个元素

 1 public V put(K key, V value) {
 2     // 如果 table 是空数组,则进行数组的扩充
 3     if (table == EMPTY_TABLE) {
 4         inflateTable(threshold);
 5     }
 6     //如果添加的 key 是 null,进行空值的节点的添加
 7     if (key == null)
 8         return putForNullKey(value);
 9         
10     //根据 key 计算 哈希值
11     int hash = hash(key);
12     
13     //根据 哈希值 计算key 所在数组下标
14     int i = indexFor(hash, table.length);
15     /**
16         1) 获取当前下标所在元素 e,判断 e 是否为 null:
17             1.1)如果 e 不为 null,则用e的hash值与添加元素hash比较:
18                 1.2)如果 hash值不一样,进行下一个元素的 hash值判断[因为有可能是一个链表],如果都不一样,进行节点添加
19                 1.3)如果 hash值一样,则进行两个元素的 key 进行比较:
20                     1.4)如果两个元素的 hash值 和 equals 都一样,则用新值替换旧值,并返回旧值;
21                     1.5)如果 hash值 一样,key 值不一样,则进行下一个元素的比较
22             
23         2) 如果 e 直接为 null,或者遍历完链表后没有找到可覆盖的元素,则进行节点的添加
24     */
25     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
26         Object k;
27         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
28             V oldValue = e.value;
29             e.value = value;
30             e.recordAccess(this);
31             return oldValue;
32         }
33     }
34 
35     modCount++;
36     //添加节点
37     addEntry(hash, key, value, i);
38     return null;
39 }
40 
41 
42 //进行 null 键的添加,null为 key 放在索引为 0 的位置,
43 /**
44  * Offloaded version of put for null keys
45  */
46 private V putForNullKey(V value) {
47     for (Entry<K,V> e = table[0]; e != null; e = e.next) {
48         if (e.key == null) {
49             V oldValue = e.value;
50             e.value = value;
51             e.recordAccess(this);
52             return oldValue;
53         }
54     }
55     modCount++;
56     addEntry(0, null, value, 0);
57     return null;
58 }
59 
60 //添加节点
61 void addEntry(int hash, K key, V value, int bucketIndex) {
62     //如果元素达到了 临界点(threshold)并且数组中当前索引不为 null,进行扩容
63     if ((size >= threshold) && (null != table[bucketIndex])) {
64         //扩容为原来的 2 倍
65         resize(2 * table.length);
66         //重新计算 key 的hash值
67         hash = (null != key) ? hash(key) : 0;
68         //重新计算所在索引
69         bucketIndex = indexFor(hash, table.length);
70     }
71     //创建节点
72     createEntry(hash, key, value, bucketIndex);
73 }
74 
75 //创建节点
76 void createEntry(int hash, K key, V value, int bucketIndex) {
77     //获取当前索引的元素 e
78     Entry<K,V> e = table[bucketIndex];
79     // 新建一个节点,新节点的下一个节点指向 e,并把新节点放在数组中
80     table[bucketIndex] = new Entry<>(hash, key, value, e);
81     //元素个数加1
82     size++;
83 }

   总结:

   添加步骤:

  (1)调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置;

  (2)如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1

  (3)如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:

      ① 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2

      ② 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:

        a、如果equals()返回false:此时key1-value1添加成功。----情况3

        b、如果equals()返回true:使用value1替换value2。

  注意:    

    (1)对于情况2和情况3:此时 key1-value1 和原来的数据以链表的方式存储;

    (2)在添加的时候,链表会以 头插法 进行插入;

    (3)在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来;

 

  2、添加一个 Map

 1 public void putAll(Map<? extends K, ? extends V> m) {
 2     int numKeysToBeAdded = m.size();
 3     if (numKeysToBeAdded == 0)
 4         return;
 5 
 6     //进行数组的初始化
 7     if (table == EMPTY_TABLE) {
 8         inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
 9     }
10 
11     /*
12      * Expand the map if the map if the number of mappings to be added
13      * is greater than or equal to threshold.  This is conservative; the
14      * obvious condition is (m.size() + size) >= threshold, but this
15      * condition could result in a map with twice the appropriate capacity,
16      * if the keys to be added overlap with the keys already in this map.
17      * By using the conservative calculation, we subject ourself
18      * to at most one extra resize.
19      */
20      //计算容量,确保数组够用
21     if (numKeysToBeAdded > threshold) {
22         int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
23         if (targetCapacity > MAXIMUM_CAPACITY)
24             targetCapacity = MAXIMUM_CAPACITY;
25         int newCapacity = table.length;
26         while (newCapacity < targetCapacity)
27             newCapacity <<= 1;
28         if (newCapacity > table.length)
29             resize(newCapacity);
30     }
31     
32     //遍历 参数的 entrySet,一个个添加到 该 Map中
33     for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
34         put(e.getKey(), e.getValue());
35 }

 

  可以看到核心方法是 put()方法,并且里面做了很多的事情。

八、扩容

   HashMap 什么时候会进行扩容呢?在执行 put() 方法时,等到给table数组添加元素之前,会进行判断,当元素个数达到临界点并且要添加的数组位置没有元素时,进行扩容,扩容为原来的2倍。

 1 public V put(K key, V value) {
 2     if (table == EMPTY_TABLE) {
 3         inflateTable(threshold);
 4     }
 5     if (key == null)
 6         return putForNullKey(value);
 7     int hash = hash(key);
 8     int i = indexFor(hash, table.length);
 9     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
10         Object k;
11         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
12             V oldValue = e.value;
13             e.value = value;
14             e.recordAccess(this);
15             return oldValue;
16         }
17     }
18 
19     modCount++;
20     addEntry(hash, key, value, i);
21     return null;
22 }
23 
24 void addEntry(int hash, K key, V value, int bucketIndex) {
25     if ((size >= threshold) && (null != table[bucketIndex])) {
26         //扩容为原来数组的2倍
27         resize(2 * table.length);
28         //重新计算当前要添加元素key的 hash值
29         hash = (null != key) ? hash(key) : 0;
30         bucketIndex = indexFor(hash, table.length);
31     }
32 
33     createEntry(hash, key, value, bucketIndex);
34 }

 

   HashMap 的扩容方法:扩容为原来的2倍,如果达到了最大容量,则无需扩容。否则创建一个新数组,将原数组中的元素重新计算hash值,判断在新数组中的位置从而添加到新数组中。

 1 void resize(int newCapacity) {
 2     Entry[] oldTable = table;
 3     int oldCapacity = oldTable.length;
 4     //如果已经达到最大值,则不需要考虑扩容
 5     if (oldCapacity == MAXIMUM_CAPACITY) {
 6         threshold = Integer.MAX_VALUE;
 7         return;
 8     }
 9     
10     //创建一个新数组
11     Entry[] newTable = new Entry[newCapacity];
12     //将原数组中的元素移动到新数组中
13     transfer(newTable, initHashSeedAsNeeded(newCapacity));
14     //重复赋值给成员变量 table
15     table = newTable;
16     //重新计算临界点
17     threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
18 }
19 
20 
21 void transfer(Entry[] newTable, boolean rehash) {
22     int newCapacity = newTable.length;
23     for (Entry<K,V> e : table) {
24         while(null != e) {
25             Entry<K,V> next = e.next;
26             //是否需要重新 hash值的计算
27             if (rehash) {
28                 e.hash = null == e.key ? 0 : hash(e.key);
29             }
30             int i = indexFor(e.hash, newCapacity);
31             e.next = newTable[i];
32             newTable[i] = e;
33             e = next;
34         }
35     }
36 }

 

九、查找元素 get() 系列

   HashMap可以根据指定的 key 来查找对应的 value。

 1 //根据 key 来获取 value
 2 public V get(Object key) {
 3     if (key == null)
 4         return getForNullKey();
 5     Entry<K,V> entry = getEntry(key);
 6 
 7     //根据 entry 来获取 value
 8     return null == entry ? null : entry.getValue();
 9 }
10 
11 //根据 null 来获取 value
12 private V getForNullKey() {
13     if (size == 0) {
14         return null;
15     }
16     for (Entry<K,V> e = table[0]; e != null; e = e.next) {
17         if (e.key == null)
18             return e.value;
19     }
20     return null;
21 }
22 
23 //根据 key 来获取 Entry
24 final Entry<K,V> getEntry(Object key) {
25     if (size == 0) {
26         return null;
27     }
28 
29     //通过 key 的 hash 值计算数组中的下标
30     int hash = (key == null) ? 0 : hash(key); 
31     
32     for (Entry<K,V> e = table[indexFor(hash, table.length)];
33          e != null;
34          e = e.next) {
35         Object k;
36         if (e.hash == hash &&
37             ((k = e.key) == key || (key != null && key.equals(k))))
38             return e;
39     }
40     return null;
41 }

 

十、删除元素 remove() 系列

   HashMap 可以根据指定的 key 来从集合中移除元素Entry,并且将已删除的元素返回。

 1 //根据 key 移除元素,如果元素删除掉,返回已删除元素的 value值
 2 public V remove(Object key) {
 3     Entry<K,V> e = removeEntryForKey(key);
 4     return (e == null ? null : e.value);
 5 }
 6 
 7 //根据 key 移除 entry,并返回已移除的 entry
 8 final Entry<K,V> removeEntryForKey(Object key) {
 9     if (size == 0) {
10         return null;
11     }
12     int hash = (key == null) ? 0 : hash(key);
13     int i = indexFor(hash, table.length);
14     Entry<K,V> prev = table[i];
15     Entry<K,V> e = prev;
16 
17     while (e != null) {
18         Entry<K,V> next = e.next;
19         Object k;
20         if (e.hash == hash &&
21             ((k = e.key) == key || (key != null && key.equals(k)))) {
22             modCount++;
23             size--;
24             if (prev == e)
25                 table[i] = next;
26             else
27                 prev.next = next;
28             e.recordRemoval(this);
29             return e;
30         }
31         prev = e;
32         e = next;
33     }
34 
35     return e;
36 }

 

十一、克隆与序列化

  1、克隆

     HashMap 实现了 Cloneable 接口,表示支持克隆。

 1 public Object clone() {
 2     HashMap<K,V> result = null;
 3     try {
 4         result = (HashMap<K,V>)super.clone();
 5     } catch (CloneNotSupportedException e) {
 6         // assert false;
 7     }
 8     if (result.table != EMPTY_TABLE) {
 9         result.inflateTable(Math.min(
10             (int) Math.min(
11                 size * Math.min(1 / loadFactor, 4.0f),
12                 // we have limits...
13                 HashMap.MAXIMUM_CAPACITY),
14            table.length));
15     }
16     result.entrySet = null;
17     result.modCount = 0;
18     result.size = 0;
19     result.init();
20     result.putAllForCreate(this);
21 
22     return result;
23 }

 

  2、序列化和反序列化

    HashMap 实现了 Serializable 接口,支持序列化和反序列化,同时使用 transient 修饰table 和 size,这样就只会序列化存有元素的部分数组,从而不序列化整个数组,节省了时间和空间,提高了效率。

 1 transient int size;
 2 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
 3 
 4 //序列化
 5 private void writeObject(java.io.ObjectOutputStream s)
 6     throws IOException
 7 {
 8     // Write out the threshold, loadfactor, and any hidden stuff
 9     s.defaultWriteObject();
10 
11     // Write out number of buckets
12     if (table==EMPTY_TABLE) {
13         s.writeInt(roundUpToPowerOf2(threshold));
14     } else {
15        s.writeInt(table.length);
16     }
17 
18     // Write out size (number of Mappings)
19     s.writeInt(size);
20 
21     // Write out keys and values (alternating)
22     if (size > 0) {
23         for(Map.Entry<K,V> e : entrySet0()) {
24             s.writeObject(e.getKey());
25             s.writeObject(e.getValue());
26         }
27     }
28 }
29 
30 //反序列化
31 private void readObject(java.io.ObjectInputStream s)
32      throws IOException, ClassNotFoundException
33 {
34     // Read in the threshold (ignored), loadfactor, and any hidden stuff
35     s.defaultReadObject();
36     if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
37         throw new InvalidObjectException("Illegal load factor: " +
38                                            loadFactor);
39     }
40 
41     // set other fields that need values
42     table = (Entry<K,V>[]) EMPTY_TABLE;
43 
44     // Read in number of buckets
45     s.readInt(); // ignored.
46 
47     // Read number of mappings
48     int mappings = s.readInt();
49     if (mappings < 0)
50         throw new InvalidObjectException("Illegal mappings count: " +
51                                            mappings);
52 
53     // capacity chosen by number of mappings and desired load (if >= 0.25)
54     int capacity = (int) Math.min(
55                 mappings * Math.min(1 / loadFactor, 4.0f),
56                 // we have limits...
57                 HashMap.MAXIMUM_CAPACITY);
58 
59     // allocate the bucket array;
60     if (mappings > 0) {
61         inflateTable(capacity);
62     } else {
63         threshold = capacity;
64     }
65 
66     init();  // Give subclass a chance to do its thing.
67 
68     // Read the keys and values, and put the mappings in the HashMap
69     for (int i = 0; i < mappings; i++) {
70         K key = (K) s.readObject();
71         V value = (V) s.readObject();
72         putForCreate(key, value);
73     }
74 }

 

十二、遍历

  HashMap 的遍历涉及了三个常用方法:获取的 value()值,获取所有的 key 值和获取所有的映射关系。

 1 //获取 HashMap 中所有的 value,可以重复无序,用 Collection 来放
 2 public Collection<V> values() {
 3     Collection<V> vs = values;
 4     return (vs != null ? vs : (values = new Values()));
 5 }
 6 //获取所有的 key,不可重复且无序,用 Set 来放
 7 public Set<K> keySet() {
 8     Set<K> ks = keySet;
 9     return (ks != null ? ks : (keySet = new KeySet()));
10 }
11 
12 //返回 key-value 的映射关系,无序且不重复,用 Set 来放
13 public Set<Map.Entry<K,V>> entrySet() {
14     return entrySet0();
15 }
16 
17 private Set<Map.Entry<K,V>> entrySet0() {
18     Set<Map.Entry<K,V>> es = entrySet;
19     return es != null ? es : (entrySet = new EntrySet());
20 }

 

 

  更多的学习查看这一篇:HashMap实现底层细节之keySet,values,entrySet的一个底层实现细节  

十三、其他方法

  下面为 HashMap 中其他的常用方法:

 1 //获取 HashMap中元素的个数
 2 public int size() {
 3     return size;
 4 }
 5 
 6 //判断 Map 是否为空(查看是否有元素)
 7 public boolean isEmpty() {
 8     return size == 0;
 9 }
10 
11 //判断 Map 是否包含指定的 key
12 public boolean containsKey(Object key) {
13     return getEntry(key) != null;
14 }
15 
16 //清除Map中所有的元素
17 public void clear() {
18     modCount++;
19     //使用 Arrays 工具类,把 table 里元素都置为 null 
20     Arrays.fill(table, null);
21     size = 0;
22 }
23 
24 //判断是否包含指定的 value值
25 public boolean containsValue(Object value) {
26     if (value == null)
27         return containsNullValue();
28 
29     Entry[] tab = table;
30     for (int i = 0; i < tab.length ; i++)
31         for (Entry e = tab[i] ; e != null ; e = e.next)
32             if (value.equals(e.value))
33                 return true;
34     return false;
35 }
36 private boolean containsNullValue() {
37     Entry[] tab = table;
38     for (int i = 0; i < tab.length ; i++)
39         for (Entry e = tab[i] ; e != null ; e = e.next)
40             if (e.value == null)
41                 return true;
42     return false;
43 }

 

 

十四、

十五、

 

本篇文章基于 JDK7(jdk1.7.0_80)进行剖析学习。

posted on 2021-05-10 12:47  格物致知_Tony  阅读(72)  评论(0编辑  收藏  举报