hashmap源码分析详解
hash算法介绍
散列表,又叫哈希表,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
我们基于一种结果尽可能随机平均分布的固定函数H为每个元素安排存储位置,这样就可以避免遍历性质的线性搜索,以达到快速存取。但是由于此随机性,也必然导致一个问题就是冲突。所谓冲突,即两个元素通过散列函数H得到的地址相同,那么这两个元素称为“同义词”。
hashmap源码分析
构造方法
public HashMap(Map<? extends K, ? extends V> m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } // 空参构造方法 public HashMap() { // 0.75f 负载因子赋值 this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } // int类型构造方法(初始化容量) public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 跟传容量的构造方法一样,只不过自定义了负载因子 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); }
int类型构造方法
public HashMap(int initialCapacity, float loadFactor) { // 判断容量initialCapacity if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + // 判断容量的最大值 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; // 负载因子判断 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; // tableSizeFor this.threshold = tableSizeFor(initialCapacity); } // initialCapacity=12 // 求2的n次方 >= initialCapacity static final int tableSizeFor(int cap) { // n = 12 -1 = 11 int n = cap - 1; // 0000 1011 = 11 // 0000 0101 = 5 // 0000 1111 = 15 = n n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
参数是Map<? extends K, ? extends V> m)
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { // 传入集合大小 int s = m.size(); if (s > 0) { // 判断Node<K,V>[] table数组 是否为null 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); } } } // 获取key的hash值 static final int hash(Object key) { int h; // int 32位 1111 1111 1111 1111 0000 0000 0000 0000 // 对象的hashcode值 ^(异或) 对象的hashcode值的高位(前16位) // 目的:提高hashcode的随机性 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } 例如:1101 1001 = hashcode , 容量 15 = 0000 1111 9 % 15 = 9 0001 1001 0111 1001 1101 1001 0000 1101 ---------- ^ 1101 0100 0000 1111 ---------- & 0000 0100
hashmap的put方法详解
public V put(K key, V value) { // 根据传入参数key,获取hashcode值 return putVal(hash(key), key, value, false, true); } // onlyIfAbsent=false // evict=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; // 判断table数组是否为空或长度为0 if ((tab = table) == null || (n = tab.length) == 0) // 初始化table , n = (tab = resize()).length; // 16 - 1 = 15 & hash值 = i // i=元素在tab数组中存储的位置 // p = tab[i] , p链表 == null // (n - 1) & hash 取余预算 目的 优化计算速度 if ((p = tab[i = (n - 1) & hash]) == null) // 创建节点,直接存放到tab[i]位置 tab[i] = newNode(hash, key, value, null); else { // tab[i]有元素的情况 Node<K,V> e; K k; // hash值相同的情况 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // 判断是否是树结构 , JDK1.8 = 红黑树优化方案 else if (p instanceof TreeNode) // 基于红黑树的插入逻辑 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { // 链表插入元素 for (int binCount = 0; ; ++binCount) { // 判断p的下一个元素是否null if ((e = p.next) == null) { // p的下一个元素 = newNode(hash, key, value, null); p.next = newNode(hash, key, value, null); // 判断当前链表的数量是否大于树结构的阈值 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st // 转换结构 // 链表 ---> 红黑树 (优化查询性能) treeifyBin(tab, hash); break; } // 当前链表包含要插入的值,结束遍历 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 判断插入的值是否存在hashmap中 if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } // 修改次数+1 ++modCount; // 判断当前数组大小是否大于阈值 if (++size > threshold) // 扩容 resize(); afterNodeInsertion(evict); return null; } final Node<K,V>[] resize() { // 数组初始值 Node<K,V>[] oldTab = table; // 扩容前的变量初始化 int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; // 扩容后的变量初始化 int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } // oldCap << 1 乘以2 = 新的容量 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) // oldThr 乘以2 = newThr newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults // 调用的无参构造方法,进入这个分支 newCap = DEFAULT_INITIAL_CAPACITY; // 负载因子 * 初始容量 = 阈值 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; // 创建一个新的*2的容量的数组 @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; // 准备重新对元素进行定位 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; // 获取第j个位置的元素 if ((e = oldTab[j]) != null) { // 清空原数组 oldTab[j] = null; // 判断原有j位置上是否有元素 if (e.next == null) // 重新计算位置,进行元素保存 // 例如 16 // [e.hash * 31 = 新元素的位置 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) // 红黑树拆分 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { // 遍历链表,将链表节点按照顺序进行分组 next = e.next; // 计算原有元素在扩容后,还在原位置 if ((e.hash & oldCap) == 0) { // old链表添加到一组 if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } // 计算原有元素在扩容后,不原位置 else { // new链表添加到一组 if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; // 原位置J + 原容量 = 新的位置 newTab[j + oldCap] = hiHead; } } } } } // 扩容之后的数组返回 return newTab; }
hashmap的删除remove方法
public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } 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 (p = tab[index = (n - 1) & hash]) != null) { Node<K,V> node = null, e; K k; V v; // hash没有冲突情况 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 定位删除的节点node 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); } } // node要删除的元素 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位置的对象下一个元素 = 删除元素的下一个元素 p.next = node.next; ++modCount; --size; afterNodeRemoval(node); return node; } } return null; }
hashmap的遍历
final Node<K,V> nextNode() { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) // 报错的原因 throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { // 寻找数组中下一个hash槽不为空的节点 do {} while (index < t.length && (next = t[index++]) == null); } return e; }
视频教程:http://www.yidiankt.com/
QQ讨论组:984370849
关注公众号-免费获取【JAVA核心知识点】!!