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     }

 

posted @ 2019-03-14 14:49  以前、以后  阅读(198)  评论(0编辑  收藏  举报