Java源码分析之HashMap(JDK1.8)
来源:互联网 发布:绫致时装官方店淘宝 编辑:程序博客网 时间:2024/05/16 18:05
转载出处:http://blog.csdn.net/eson_15/article/details/51154989
一、HashMap概述
HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现。与HashTable主要区别为不支持同步和允许null作为key和value。由于HashMap不是线程安全的,如果想要线程安全,可以使用ConcurrentHashMap代替。
二、HashMap数据结构
HashMap的底层是哈希数组,数组元素为Entry。HashMap通过key的hashCode来计算hash值,当hashCode相同时,通过“拉链法”解决冲突,如下图所示。
相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。原本Map.Entry接口的实现类Entry改名为了Node。转化为红黑树时改用另一种实现TreeNode。
Node类
static class Node<K,V> implements Map.Entry<K,V> { final int hash; // 哈希值 final K key; V value; Node<K,V> next; // 指向下一个节点 Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + "=" + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry<?,?> e = (Map.Entry<?,?>)o; if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue())) return true; } return false; } }
- 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
TreeNode类
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
HashMap就是这样一个Entry(包括Node和TreeNode)数组,Node对象中包含键、值和hash值,next指向下一个Entry,用来处理哈希冲突。TreeNode对象包含指向父节点、子节点和前一个节点(移除对象时使用)的指针,以及表示红黑节点的boolean标识。
三、HashMap源码分析
1. 主要属性
transient Node<K,V>[] table; // 哈希数组 transient Set<Map.Entry<K,V>> entrySet; // entry缓存Set transient int size; // 元素个数 transient int modCount; // 修改次数 int threshold; // 阈值,等于加载因子*容量,当实际大小超过阈值则进行扩容 final float loadFactor; // 加载因子,默认值为0.75
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
2. 构造方法
以下是HashMap的几个构造方法。
/** * 根据初始化容量和加载因子构建一个空的HashMap. */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } /** * 使用初始化容量和默认加载因子(0.75). */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * 使用默认初始化大小(16)和默认加载因子(0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * 用已有的Map构造一个新的HashMap. */ public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); }
- 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
3. 数据存取
- putAll方法
public void putAll(Map<? extends K, ? extends V> m) { putMapEntries(m, true); } /** * Implements Map.putAll and Map constructor * * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). */ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { int s = m.size(); if (s > 0) { if (table == null) { // pre-size float ft = ((float)s / loadFactor) + 1.0F; int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); if (t > threshold) threshold = tableSizeFor(t); } else if (s > threshold) resize(); for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict); // put核心方法 } } }
- 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
- put方法
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 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) // table为空或length为0 n = (tab = resize()).length; // 初始化 if ((p = tab[i = (n - 1) & hash]) == null) // 如果hash所在位置为null,直接put tab[i] = newNode(hash, key, value, null); else { // tab[i]有元素,遍历节点后添加 Node<K,V> e; K k; // 如果hash、key都相等,直接覆盖 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); // 链表节点大于阈值8,调用treeifyBin方法,当tab.length大于64将链表改为红黑树 // 如果tab.length < 64或tab为null,则调用resize方法重构链表. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } // hash、key都相等,此时节点即要更新节点 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 当前节点e = p.next不为null,表示链表中原本存在相同的key,则返回oldValue if (e != null) { // existing mapping for key V oldValue = e.value; // onlyIfAbsent值为false,参数主要决定存在相同key时是否执行替换 if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) // 检查是否超过阈值 resize(); afterNodeInsertion(evict); return null; // 原HashMap中不存在相同的key,插入键值对后返回null }
- 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
- get方法
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * Implements Map.get and related methods * * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) // 红黑树 return ((TreeNode<K,V>)first).getTreeNode(hash, key); // 链表 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; } // 遍历红黑树搜索节点 /** * Calls find for root node. */ final TreeNode<K,V> getTreeNode(int h, Object k) { return ((parent != null) ? root() : this).find(h, k, null); } /** * Returns root of tree containing this node. */ final TreeNode<K,V> root() { for (TreeNode<K,V> r = this, p;;) { if ((p = r.parent) == null) return r; r = p; } } /** * Finds the node starting at root p with the given hash and key. * The kc argument caches comparableClassFor(key) upon first use * comparing keys. */ final TreeNode<K,V> find(int h, Object k, Class<?> kc) { TreeNode<K,V> p = this; do { int ph, dir; K pk; TreeNode<K,V> pl = p.left, pr = p.right, q; if ((ph = p.hash) > h) // 当前节点hash大 p = pl; // 查左子树 else if (ph < h) // 当前节点hash小 p = pr; // 查右子树 else if ((pk = p.key) == k || (k != null && k.equals(pk))) return p; // hash、key都相等,即找到,返回当前节点 else if (pl == null) // hash相等,key不等,左子树为null,查右子树 p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0) p = (dir < 0) ? pl : pr; else if ((q = pr.find(h, k, kc)) != null) return q; else p = pl; } while (p != null); return null; }
- 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
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- remove方法
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * Implements Map.remove and related methods * * @param hash hash for key * @param key the key * @param value the value to match if matchValue, else ignored * @param matchValue if true only remove if value is equal * @param movable if false do not move other nodes while removing * @return the node, or null if none */ final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { Node<K,V> node = null, e; K k; V v; // 直接命中 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null) { if (p instanceof TreeNode) // 在红黑树中查找 node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { // 在链表中查找 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } // 命中后删除 if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) // 在红黑树中删除节点 ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable); else if (node == p) // 链表首节点删除 tab[index] = node.next; else // 多节点链表删除 p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }
- 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
- clear方法
/** * Removes all of the mappings from this map. * The map will be empty after this call returns. */ public void clear() { Node<K,V>[] tab; modCount++; if ((tab = table) != null && size > 0) { size = 0; for (int i = 0; i < tab.length; ++i) tab[i] = null; // 把哈希数组中所有位置都赋为null } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
四、总结
本文从源码入手,简单地分析了HashMap底层的结构和实现。在源码分析部分主要分析了常用的几个方法,还有一些方法比如调整哈希表大小的resize、将链表转化为红黑树的treeify以及逆操作untreeify等,在此不再详细分析。红黑树部分的代码只理解了大概,实现细节上还有待进一步阅读分析。
- Java源码分析之HashMap(JDK1.8)
- Java源码分析之HashMap(JDK1.8)
- JDK1.8源码分析之HashMap
- 【Java】HashMap源码分析(JDK1.8)
- JDK1.8源码学习之 HashMap.java
- JDK1.8 HashMap源码分析
- JDK1.8 HashMap源码分析
- 【jdk1.8】HashMap源码分析
- jdk1.8 hashMap源码分析
- JDK1.8 HashMap 源码分析
- JDK1.8 HashMap源码分析
- HashMap源码分析 JDK1.8
- jdk1.8 HashMap源码分析
- java源码分析之HashMap(jdk1.6)
- JDK1.7之 HashMap 源码分析
- Java中HashMap底层实现原理(JDK1.8)源码分析
- java集合(4):HashMap源码分析(jdk1.8)
- Java中HashMap底层实现原理(JDK1.8)源码分析
- 新手怎样发表期刊
- 理解矩阵1
- 算法
- Android原生音量控制
- Aspose.Total 套包组件介绍 | 附下载
- Java源码分析之HashMap(JDK1.8)
- Android四大组件之一 BroadcastReceiver解析
- 理解矩阵2
- 图像风格化总结
- android动画
- Apache服务器下ThinkPHP / ThinkCMF 伪静态解决方案
- hdu 5718 Oracle
- linux跟本地文件交互
- Java compiler level does not match the version of the installed Java project facet