摘要
HashMap的内部表示在Java8中得到很大的改进,比如说在Java7中实现HashMap需要1k行代码,而在Java8中需要2K行代码。在Java8中内部仍然以数组实现,但是以节点(Node)来作为Entry存储信息,并且同样也包括链表。
一、重要的概念
从结构实现来讲,HashMap是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下如所示。
Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。上图中的每个黑色圆点就是一个Node对象。
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;
}
}
Node是最核心的内部类,它包装了key-value键值对,允许调用setValue方法直接改变Node的value域。(1.8的ConcurrentHashMap中对Node的value和next属性设置了volatile同步锁(与JDK7的Segment相同),它不允许调用setValue方法直接改变Node的value域,它增加了find方法辅助map.get()方法。)
它实现了Map.Entry接口。其内部的变量含义也很明确,hash值、key\value对和实现链表和红黑树所需要的指针索引。
既然知道了HashMap的基本结构,那么这些变量的默认值都是多少呢?我们再看一下HashMap定义的一些常量:
//默认的初始容量为16,必须是2的幂次
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//最大容量即2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//当put一个元素时,其链表长度达到8时将链表转换为红黑树
static final int TREEIFY_THRESHOLD = 8;
//链表长度小于6时,解散红黑树
static final int UNTREEIFY_THRESHOLD = 6;
//默认的最小的扩容量64,为避免重新扩容冲突,至少为4 * TREEIFY_THRESHOLD=32,即默认初始容量的2倍