HashMap源码解析

1.hashmap
1.1hashmap介绍
实现map接口,一种重要的数据结构,采用key-value键值对的形式来保存,可以都为null,但key值不能相同,继承map类,实现map接口
JDK1.7采用数组加链表,JDK1.8采用数组+链表+红黑树
public class HashMap<K,V> extends AbstractMap<K,V>
           implements Map<K,V>, Cloneable, Serializable
 
2.hashmap实现
2.1创建一个hashmap用例 
HashMap<String,Double> map=new HashMap<>();

    当我们debug时,java首先进入Hashmap类,进行方法构建

 

 static final float DEFAULT_LOAD_FACTOR = 0.75f
/*
  * 当新建一个hsahmap首先进的方法,提交负载因子
  * */
  public HashMap() {
      this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
  }

 

  loadFactor:事先设定好的负载因子,一般选0.75,当数组扩容时的最低判断
  DEFAULT_LOAD_FACTOR:事先设定值
  注意:

 

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

hashmap中几个同名的构造方法,用来指定初始化的数组长度及构造因子大小,能够用来做最小的数组扩容判断

 

2.2实现put()方法
当我们往hashmap中放入值时,例如:
1 map.put("k1",0.2);
2       map.put("g4",0.4);
3       map.put("f2",0.1);
View Code
调用源码中的方法:
1 public V put(K key, V value) {
2       return putVal(hash(key), key, value, false, true);
3   }
View Code

 

先计算key的hash值
1 static final int hash(Object key) {
2       int h;
3       return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
4   }
View Code
当key不为null时,计算hash

  1 (h = key.hashCode()) ^ (h >>> 16): 

1. h >>> 16 是什么,有什么用?
取出高16位,
 1 0000 0100 1011 0011 1101 1111 1110 0001 >>> 16 0000 0000 0000 0000 0000 0100 1011 0011 
2. 为什么 h = key.hashCode()) 与 (h >>> 16) 异或
讲到这里还要看一个方法indexFor,在jdk1.7中有indexFor(int h, int length)方法。jdk1.8里没有,但原理没变。下面看下1.7源码
1 static int indexFor(int h, int length) {
2       return h & (length-1);
3   }
View Code
1.8中用tab[(n - 1) & hash]代替但原理一样。
 
这个方法返回值就是数组下标。我们平时用map大多数情况下map里面的数据不是很多。这里与(length-1)相&,
hasCode()转换

 

 1 public int hashCode() {
 2       int h = hash;
 3       if (h == 0 && value.length > 0) {
 4           char val[] = value;
 5           //
 6           for (int i = 0; i < value.length; i++) {
 7               h = 31 * h + val[i];
 8           }
 9           hash = h;
10       }
11       return h;
12   }
View Code

 

 

 putval()方法

 1 /**
 2  * Implements Map.put and related methods
 3  *
 4  * @param hash hash for key
 5  * @param key the key
 6  * @param value the value to put
 7  * @param onlyIfAbsent if true, don't change existing value
 8  * @param evict if false, the table is in creation mode.
 9  * @return previous value, or null if none
10  */
11 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                boolean evict) {
13     Node<K,V>[] tab; Node<K,V> p; int n, i;
14     //table就是一个Entry容器,用来存放键值对
15     /*
16     *当entry为空时,会初始化序列进入resize()方法,用分配原始的数组值来
17     *
18     *
19     ****
20     */
21      // 如果存储元素的table为空,则进行必要字段的初始化
22     if ((tab = table) == null || (n = tab.length) == 0)
23         n = (tab = resize()).length;
24         // 如果根据hash值获取的结点为空,则新建一个结点
25     if ((p = tab[i = (n - 1) & hash]) == null) 
26         tab[i] = newNode(hash, key, value, null); // 此处 & 代替了 % (除法散列法进行散列)
27    // 这里的p结点是根据hash值算出来对应在数组中的元素
28       else {
29         Node<K,V> e; K k;
30         // 如果新插入的结点和table中p结点的hash值,key值相同的话
31         if (p.hash == hash &&
32             ((k = p.key) == key || (key != null && key.equals(k))))
33             e = p;
34             // 如果是红黑树结点的话,进行红黑树插入  
35         else if (p instanceof TreeNode)
36             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
37         else {
38             for (int binCount = 0; ; ++binCount) {
39                    // 代表这个单链表只有一个头部结点,则直接新建一个结点即可
40                 if ((e = p.next) == null) {
41                     p.next = newNode(hash, key, value, null);
42                             // 链表长度大于8时,将链表转红黑树
43                     if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
44                         treeifyBin(tab, hash);
45                     break;
46                 }
47                 if (e.hash == hash &&
48                     ((k = e.key) == key || (key != null && key.equals(k))))
49                     break;
50                        // 及时更新p
51                 p = e;
52             }
53         }
54          // 如果存在这个映射就覆盖
55         if (e != null) { // existing mapping for key
56             V oldValue = e.value;
57                 // 判断是否允许覆盖,并且value是否为空
58             if (!onlyIfAbsent || oldValue == null)
59                 e.value = value;
60             afterNodeAccess(e);  // 回调以允许LinkedHashMap后置操作
61             return oldValue;
62         }
63     }
64     ++modCount; // 更改操作次数
65     if (++size > threshold)   // 大于临界值
66        // 将数组大小设置为原来的2倍,并将原先的数组中的元素放到新数组中
67             // 因为有链表,红黑树之类,因此还要调整他们
68          resize();
69            // 回调以允许LinkedHashMap后置操作
70     afterNodeInsertion(evict);
71     return null;
72 }

 

 

 

 

 

 

 

 

posted @ 2020-04-13 11:12  无语的风  阅读(39)  评论(0)    收藏  举报