hashmap的基本原理

hashMap基本原理

hashMap基本注意点

  1. key不可重复,值可重复

  2. 底层是哈希表

  3. 线程不安全

  4. 允许key为null,value也可为null

  5. jdk1.8 链表长度大于8的时候,会将链表转换为红黑树

hashMap的数据结构

 

HashMap Node节点

 1 //node是个静态的内部类,存着key-vale的值 
 2 static class Node<K,V> implements Map.Entry<K,V> {
 3         final int hash;//保存该桶的hash值
 4         final K key;//不可改变的hash值
 5         V value;
 6         Node<K,V> next;//指向桶hash的下一节点
 7  8         Node(int hash, K key, V value, Node<K,V> next) {
 9             this.hash = hash;
10             this.key = key;
11             this.value = value;
12             this.next = next;
13         }
14 15         public final K getKey()        { return key; }
16         public final V getValue()      { return value; }
17         public final String toString() { return key + "=" + value; }
18 19         public final int hashCode() {
20             return Objects.hashCode(key) ^ Objects.hashCode(value);
21         }
22 23         public final V setValue(V newValue) {
24             V oldValue = value;
25             value = newValue;
26             return oldValue;
27         }
28 29         public final boolean equals(Object o) {
30             if (o == this)
31                 return true;
32             if (o instanceof Map.Entry) {
33                 Map.Entry<?,?> e = (Map.Entry<?,?>)o;
34                 if (Objects.equals(key, e.getKey()) &&
35                     Objects.equals(value, e.getValue()))
36                     return true;
37             }
38             return false;
39         }
40     }

 

如何通过key大hashcode来确定桶的位置

1  //这个确定了hash的值,这是第一步
2 static final int hash(Object key) {
3         int h;
4         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
5     }
6 //后面的putVal会通过数组的长度与hash的值来最终确定桶的位置
7 //但是存入hash值还是hash值
8 //i为准确数组的位置
9 if ((p = tab[i = (n - 1) & hash]) == null)

 

hashMap的基本变量

1 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认桶的数量
2 static final int MAXIMUM_CAPACITY = 1 << 30;//最大值
3 static final float DEFAULT_LOAD_FACTOR = 0.75f;//负载因子
4 int threshold;//能够容纳key_value的数量
5 int size;//key的个数

 

threshold=负载因子*length,负载因子越大,所能容纳的元素个数越多

注意

如果空间比较大,要求速度比较快,那么既可以将负载因子调小,以降低冲突

hashMap的put方法实现

 1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                    boolean evict) {
 3         Node<K,V>[] tab; Node<K,V> p; int n, i;
 4     //判断table是否为空,我空就创建一个并获得数组的长度
 5         if ((tab = table) == null || (n = tab.length) == 0)
 6             n = (tab = resize()).length;
 7     //判断计算出来的桶位置是否为空,为空的化,直接new一个值放在这个位置
 8         if ((p = tab[i = (n - 1) & hash]) == null)
 9             tab[i] = newNode(hash, key, value, null);
10         else {
11             //如果这个桶有的化
12             Node<K,V> e; K k;
13             //判断如果key值相同,就直接替代
14             if (p.hash == hash &&
15                 ((k = p.key) == key || (key != null && key.equals(k))))
16                 e = p;
17             else if (p instanceof TreeNode)//如果为红黑树,就直接放入
18                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
19             else {
20                 for (int binCount = 0; ; ++binCount) {//放值
21                     if ((e = p.next) == null) {
22                         p.next = newNode(hash, key, value, null);
23                         if (binCount >= TREEIFY_THRESHOLD - 1) // 如果链表长度大于8的化,就直接变为树
24                             treeifyBin(tab, hash);
25                         break;
26                     }//判断索引每个元素是否key相同,直接替代
27                     if (e.hash == hash &&
28                         ((k = e.key) == key || (key != null && key.equals(k))))
29                         break;
30                     p = e;
31                 }
32             }
33             //替换原来的值,并返回原来的值
34             if (e != null) { // existing mapping for key
35                 V oldValue = e.value;
36                 if (!onlyIfAbsent || oldValue == null)
37                     e.value = value;
38                 afterNodeAccess(e);
39                 return oldValue;
40             }
41         }
42         ++modCount;//没有相同的key,结构修改次数
43         if (++size > threshold)
44             resize();
45         afterNodeInsertion(evict);
46         return null;
47     }

 

  1. 判断table是否为空

  2. 计算的到的位置,是否有值

  3. 判断是否同一个key

  4. 加入一个值后,判断该链表是否大于8,转换红黑数结构

  5. 加入一个新key,记录结构变化次数,

  6. 替换key的value会返回老的value

Hashmap的get方法

 1 final Node<K,V> getNode(int hash, Object key) {
 2         Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
 3     //判断不能为空
 4         if ((tab = table) != null && (n = tab.length) > 0 &&
 5             (first = tab[(n - 1) & hash]) != null) {
 6             if (first.hash == hash && // always check first node
 7                 ((k = first.key) == key || (key != null && key.equals(k))))
 8                 return first;//第一是就直接返回
 9             if ((e = first.next) != null) {
10                 if (first instanceof TreeNode)//树状
11                     return ((TreeNode<K,V>)first).getTreeNode(hash, key);
12                 do {//链表遍历查找
13                     if (e.hash == hash &&
14                         ((k = e.key) == key || (key != null && key.equals(k))))
15                         return e;
16                 } while ((e = e.next) != null);
17             }
18         }
19         return null;
20     }

 

hashMap的扩容机制

resize方法

  
 1  final Node<K,V>[] resize() {
 2         Node<K,V>[] oldTab = table;
 3         int oldCap = (oldTab == null) ? 0 : oldTab.length;
 4         int oldThr = threshold;
 5         int newCap, newThr = 0;
 6         if (oldCap > 0) {
 7             if (oldCap >= MAXIMUM_CAPACITY) {
 8                 threshold = Integer.MAX_VALUE;//阀值直接弄到最大
 9                 return oldTab;//达到最大限度
10             }
11             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
12                      oldCap >= DEFAULT_INITIAL_CAPACITY)
13                 newThr = oldThr << 1; // 扩容2倍
14         }
15         else if (oldThr > 0) // 初始容量置于阀值
16             newCap = oldThr;
17         else {               //如果是0表示使用的默认值
18             newCap = DEFAULT_INITIAL_CAPACITY;
19             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
20         }
21         if (newThr == 0) {//得到新的阀值
22             float ft = (float)newCap * loadFactor;
23             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
24                       (int)ft : Integer.MAX_VALUE);
25         }
26         threshold = newThr;
27         @SuppressWarnings({"rawtypes","unchecked"})
28         Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//新的空的表
29         table = newTab;
30         if (oldTab != null) {
31             for (int j = 0; j < oldCap; ++j) {
32                 Node<K,V> e;
33                 if ((e = oldTab[j]) != null) {
34                     //获得该节点并置空原节点
35                     oldTab[j] = null;
36                     if (e.next == null)
37                         //重新计算位置并放入
38                         newTab[e.hash & (newCap - 1)] = e;
39                     else if (e instanceof TreeNode)//该节点是红黑树
40                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
41                     else { // 普通链表
42                         Node<K,V> loHead = null, loTail = null;
43                         Node<K,V> hiHead = null, hiTail = null;
44                         Node<K,V> next;
45                         do {
46                             next = e.next;
47                             if ((e.hash & oldCap) == 0) {//如果为0则原来的位置不是则原来的位置加一倍
48                                 if (loTail == null)
49                                     loHead = e;
50                                 else
51                                     loTail.next = e;
52                                 loTail = e;
53                             }
54                             else {
55                                 if (hiTail == null)
56                                     hiHead = e;
57                                 else
58                                     hiTail.next = e;
59                                 hiTail = e;
60                             }
61                         } while ((e = next) != null);
62                         if (loTail != null) {
63                             loTail.next = null;
64                             newTab[j] = loHead;
65                         }
66                         if (hiTail != null) {
67                             hiTail.next = null;
68                             newTab[j + oldCap] = hiHead;
69                         }
70                     }
71                 }
72             }
73         }
74         return newTab;
75     }

 


  1. 如果数组的长度达到最大,那么阀值直接弄到整数的最大值

  2. 数组长度可以超过最大值

  3. 扩容时不需要计算hash值,但要重新计算位置

  4. 在链表时,hash的高位与old的数组长度&计算为0,则为原来的位置,1则为原来的位置+1倍数组长度;

 

参考文档

https://www.cnblogs.com/duodushuduokanbao/p/9492952.html

 

posted @ 2019-07-17 21:25  胡萝卜88号  阅读(400)  评论(0)    收藏  举报