JDK1.8源码之HashMap(上)
Posted Ideology First
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK1.8源码之HashMap(上)相关的知识,希望对你有一定的参考价值。
//链表
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;
}
//...
}
//红黑树
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);
}
//...
}
//存储元素的table
transient Node<K,V>[] table;
//默认数组容量 16 - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//最大容量 - MUST be a power of two <= 1<<30.
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认负载因子.
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表转为红黑树的阈值
static final int TREEIFY_THRESHOLD = 8;
//红黑树转为链表的阈值
static final int UNTREEIFY_THRESHOLD = 6;
//可以被树化的最小表容量
static final int MIN_TREEIFY_CAPACITY = 64
* Because TreeNodes are about twice the size of regular nodes, we
* use them only when bins contain enough nodes to warrant use
* (see TREEIFY_THRESHOLD). And when they become too small (due to
* removal or resizing) they are converted back to plain bins.
即单个 TreeNode 需要占用的空间大约是普通 Node 的两倍,所以只有当包含足够多的 Nodes 时才会转成 TreeNodes,这个足够多的标准就是由 TREEIFY_THRESHOLD 的值(默认值8)决定的。而当桶中节点数由于移除或者 resize (扩容) 变少后,红黑树会转变为普通的链表,这个阈值是 UNTREEIFY_THRESHOLD(默认值6)。
所以这是基于时间和空间的平衡考虑。
上面说到,当链表长度达到阈值8的时候会转为红黑树,但是红黑树退化为链表的阈值却是6,为什么不是小于8就退化呢?比如说7的时候就退化,偏偏要小于或等于6?这主要是一个过渡,避免链表和红黑树之间频繁的转换。如果阈值是7的话,删除一个元素红黑树就必须退化为链表,增加一个元素就必须树化,来回不断的转换结构无疑会降低性能,所以阈值才不设置的那么临界。
在源码的注释中有这么一段:
* Ignoring variance, the expected occurrences of list size k are
* (exp(-0.5) * pow(0.5, k) / factorial(k)).
* The first values are:
* 0: 0.60653066
* 1: 0.30326533
* 2: 0.07581633
* 3: 0.01263606
* 4: 0.00157952
* 5: 0.00015795
* 6: 0.00001316
* 7: 0.00000094
* 8: 0.00000006
* more: less than 1 in ten million
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);
}
//Returns a power of two size for the given target capacity.
static final int tableSizeFor(int cap) {
int n = cap - 1;
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;
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
(n - 1) & hash
0000 0000 0000 0101 0011 0010 0010 1000
0000 0000 0101 0000 0011 0010 0010 1000
0000 0000 0000 0101 0011 0010 0010 1000
& 0011 1111
---------------------------------------
0010 1000
0000 0000 0101 0000 0011 0010 0010 1000
& 0011 1111
---------------------------------------
0010 1000
0000 0000 0000 0101 0011 0010 0010 1000
^ 0000 0000 0000 0000 0000 0000 0000 0101
---------------------------------------
0000 0000 0000 0101 0011 0010 0010 1101
0000 0000 0101 0000 0011 0010 0010 1000
^ 0000 0000 0000 0000 0000 0000 0101 0000
---------------------------------------
0000 0000 0101 0000 0011 0010 0111 1000
0000 0000 0000 0101 0011 0010 0010 1101
0011 1111
---------------------------------------
0010 1101
0000 0000 0101 0000 0011 0010 0111 1000
0011 1111
---------------------------------------
0011 1000
以上是关于JDK1.8源码之HashMap(上)的主要内容,如果未能解决你的问题,请参考以下文章