HashMap源码理解与分析
/** | |
* HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现。与HashTable主要区别为不支持同步和允许null作为key和value。 | |
* HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。 | |
* 如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。 | |
* 在JDK1.6中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。 | |
* 但是当位于一个数组中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。 | |
* 而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值8时,将链表转换为红黑树,这样大大减少了查找时间。 | |
* 原本Map.Entry接口的实现类Entry改名为了Node。转化为红黑树时改用另一种实现TreeNode。 | |
*/ |
1 public class HashMap<K, V> extends AbstractMap<K, V> 2 implements Map<K, V>, Cloneable, Serializable { 3 4 private static final long serialVersionUID = 362498820763181265L; 5 6 7 /** 8 * 默认的初始容量(容量为HashMap中槽的数目)是16,且实际容量必须是2的整数次幂。 9 */ 10 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 11 12 /** 13 * 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换) 14 */ 15 static final int MAXIMUM_CAPACITY = 1 << 30; 16 17 /** 18 * 默认装填因子0.75,如果当前键值对个数 >= HashMap最大容量*装填因子,进行rehash操作 19 */ 20 static final float DEFAULT_LOAD_FACTOR = 0.75f; 21 /** 22 * JDK1.8 新加,Entry链表最大长度,当桶中节点数目大于该长度时,将链表转成红黑树存储; 23 */ 24 static final int TREEIFY_THRESHOLD = 8; 25 26 /** 27 * JDK1.8 新加,当桶中节点数小于该长度,将红黑树转为链表存储; 28 */ 29 static final int UNTREEIFY_THRESHOLD = 6; 30 31 /** 32 * 桶可能被转化为树形结构的最小容量。当哈希表的大小超过这个阈值,才会把链式结构转化成树型结构,否则仅采取扩容来尝试减少冲突。 33 * 应该至少4*TREEIFY_THRESHOLD来避免扩容和树形结构化之间的冲突。 34 */ 35 static final int MIN_TREEIFY_CAPACITY = 64; 36 37 /** 38 * JDK1.6用Entry描述键值对,JDK1.8中用Node代替Entry 39 */ 40 static class Node<K, V> implements Map.Entry<K, V> { 41 // hash存储key的hashCode 42 final int hash; 43 // final:一个键值对的key不可改变 44 final K key; 45 V value; 46 //指向下个节点的引用 47 Node<K, V> next; 48 49 //构造函数 50 Node(int hash, K key, V value, Node<K, V> next) { 51 this.hash = hash; 52 this.key = key; 53 this.value = value; 54 this.next = next; 55 } 56 57 public final K getKey() { 58 return key; 59 } 60 61 public final V getValue() { 62 return value; 63 } 64 65 public final String toString() { 66 return key + "=" + value; 67 } 68 69 public final int hashCode() { 70 return Objects.hashCode(key) ^ Objects.hashCode(value); 71 } 72 73 public final V setValue(V newValue) { 74 V oldValue = value; 75 value = newValue; 76 return oldValue; 77 } 78 public final boolean equals(Object o) { 79 if (o == this) 80 return true; 81 if (o instanceof Map.Entry) { 82 Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; 83 if (Objects.equals(key, e.getKey()) && 84 Objects.equals(value, e.getValue())) 85 return true; 86 } 87 return false; 88 } 89 }
/** | |
* HashMap中键值对的存储形式为链表节点,hashCode相同的节点(位于同一个桶)用链表组织 | |
* hash方法分为三步: | |
* 1.取key的hashCode | |
* 2.key的hashCode高16位异或低16位 | |
* 3.将第一步和第二步得到的结果进行取模运算。 | |
*/ |
1 static final int hash(Object key) { 2 int h; 3 //计算key的hashCode, h = Objects.hashCode(key) 4 //h >>> 16表示对h无符号右移16位,高位补0,然后h与h >>> 16按位异或 5 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 6 } 7 8 /** 9 * 如果参数x实现了Comparable接口,返回参数x的类名,否则返回null 10 */ 11 static Class<?> comparableClassFor(Object x) { 12 if (x instanceof Comparable) { 13 Class<?> c; 14 Type[] ts, as; 15 Type t; 16 ParameterizedType p; 17 if ((c = x.getClass()) == String.class) // bypass checks 18 return c; 19 if ((ts = c.getGenericInterfaces()) != null) { 20 for (int i = 0; i < ts.length; ++i) { 21 if (((t = ts[i]) instanceof ParameterizedType) && 22 ((p = (ParameterizedType) t).getRawType() == 23 Comparable.class) && 24 (as = p.getActualTypeArguments()) != null && 25 as.length == 1 && as[0] == c) // type arg is c 26 return c; 27 } 28 } 29 } 30 return null; 31 } 32 33 /** 34 * 如果x的类型为kc,则返回k.compareTo(x),否则返回0 35 */ 36 @SuppressWarnings({"rawtypes", "unchecked"}) // for cast to Comparable 37 static int compareComparables(Class<?> kc, Object k, Object x) { 38 return (x == null || x.getClass() != kc ? 0 : 39 ((Comparable) k).compareTo(x)); 40 } 41 42 /** 43 * 结果为>=cap的最小2的自然数幂 44 */ 45 static final int tableSizeFor(int cap) { 46 //先移位再或运算,最终保证返回值是2的整数幂 47 int n = cap - 1; 48 n |= n >>> 1; 49 n |= n >>> 2; 50 n |= n >>> 4; 51 n |= n >>> 8; 52 n |= n >>> 16; 53 return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; 54 }
/* ---------------- Fields -------------- */ |
1 /** 2 * 哈希桶数组,分配的时候,table的长度总是2的幂 3 */ 4 transient Node<K, V>[] table; 5 6 /** 7 * HashMap将数据转换成set的另一种存储形式,这个变量主要用于迭代功能 8 */ 9 transient Set<Map.Entry<K, V>> entrySet; 10 11 /** 12 * 实际存储的数量,则HashMap的size()方法,实际返回的就是这个值,isEmpty()也是判断该值是否为0 13 */ 14 transient int size; 15 16 /** 17 * hashmap结构被改变的次数,fail-fast机制 18 */ 19 transient int modCount; 20 21 /** 22 * HashMap的扩容阈值,在HashMap中存储的Node键值对超过这个数量时,自动扩容容量为原来的二倍 23 * 24 * @serial 25 */ 26 int threshold; 27 28 /** 29 * HashMap的负加载因子,可计算出当前table长度下的扩容阈值:threshold = loadFactor * table.length 30 * 31 * @serial 32 */ 33 final float loadFactor;
具体源码参考地址:
https://github.com/wupeixuan/JDKSourceCode1.8/blob/master/src/java/util/HashMap.java
千里路我只陪你一程,从此艳阳风雪我不问!