e2和cpa都是什么呢?不知道捏
一、简单介绍HashMap的底层实现
HashMap总体是数组+链表的存储结构
基于hashCode,HashMap有着十分出色的查找能力,可以快速寻找内容,但是存储的数据不能重复,也不能排序或者使用index进行遍历
二、HashMap的存储结构
HashMap是基于数组+链表存储的数据结构,在jdk1.8之后,当数组长度大于64且链表长度大于8时,链表会被自动转换为红黑树
HashMap每一个数组都由一个个叫做node的小单元构成,数组初始长度被设置为16
1
| static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
|
node元素在链表和红黑树中有不同的结构,红黑树中的TreeNode继承于链表中的Node
1.链表中的源码如下:
1 2 3 4 5
| static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next;
|
final int hash
为key的hash值,通过调用hashCode()
方法并进行一系列计算得到
final K key
为key
V value
为key对应的value
Node<K,V> next
记录着下一个节点的地址值,以此来实现链表
2.红黑树中的源码如下:
1 2 3 4 5 6 7 8 9
| static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); }
|
由于继承了链表中的Node,TreeNode也一样拥有以下内容
final int hash
为key的hash值
final K key
为key
V value
为key对应的value
Node<K,V> next
记录着下一个节点的地址值
同时TreeNode还有以下内容,用来实现红黑树
TreeNode<K,V> parent
记录父节点的地址值
TreeNode<K,V> left
记录左子节点的地址值
TreeNode<K,V> right
记录右子节点的地址值
TreeNode<K,V> prev
记录上一个节点的地址值
boolean red
记录节点的颜色,默认为红色
三、HashMap的put操作过程
我们假定运行如下代码
1 2 3 4 5 6
| HashMap<String,Integer> hm = new HashMap<>(); hm.put("aaa" , 111); hm.put("bbb" , 222); hm.put("ccc" , 333); hm.put("ddd" , 444); hm.put("eee" , 555);
|
进行put操作时,至少要考虑三种情况:
- 数组位置为null
- 数组位置不为null,key出现了重复,进行元素覆盖
- 数组位置不为null,key不重复,挂在下面形成链表,进而形成红黑树
put操作源码如下,输入为key和value
1 2 3
| public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
|
返回值为被覆盖元素的值,如果没有进行覆盖,就返回null
putVal(hash(key), key, value, false, true)
此方法中调用了hash()
方法,方法如下
1 2 3 4
| static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
|
此方法即利用key计算出hash值,再把hash值进行另外的一些处理,并进行返回
1.数组位置为null
putVal
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
其中final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)
方法块中,参数hash为刚刚求出的hash值,key为键,value为值,onlyIfAbsent为如果键发生重复,是否保留
2.数组位置不为null,key出现了重复,进行元素覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
3.数组位置不为null,key不重复,挂在下面形成链表,进而形成红黑树
putVal
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
此语句中包含三个命题:
p.hash == hash
左边为数组中键值对的hash值,右边为要添加键值对的hash值,如果key不一样,则返回false