一、关于源码:
1、参数列表
// HashMap默认容量,采用位运算,运算速度更快
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// HashMap的默认最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 链表转为红黑树的条件
// 1.链表的长度>=8
// 2.数组的长度>=64 所以链表要变为红黑树,最少需要进行两次扩容resize()16->32->64
static final int TREEIFY_THRESHOLD = 8;
// 反树化:如果红黑树上的节点<=6,那就将红黑树转回链表
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
源码解析 :
- DEFAULT_INITIAL_CAPACITY:HashMap默认容量2^4 ->16,采用位运算,运算速度更快
- MAXIMUM_CAPACITY:HashMap的默认最大容量
- DEFAULT_LOAD_FACTOR:默认负载因子,当数组容量达到最大值的0.75时进行扩容
- TREEIFY_THRESHOLD:树化:当桶中链表的长度达到8时并且MIN_TREEIFY_CAPACITY(树化需要达到的最小桶个数)达到64时进行树化
- UNTREEIFY_THRESHOLD: 反树化,如果红黑树上的节点<=6就将红黑树转回链表
- MIN_TREEIFY_CAPACITY:树化需要达到的最小桶个数
2、内部静态类: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;
return o instanceof Map.Entry<?, ?> e
&& Objects.equals(key, e.getKey())
&& Objects.equals(value, e.getValue());
}
}
源码解析 :
实现了链表的基本操作
3、哈希函数(散列函数)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
源码解析 :
可以将源码改写为以下代码更方便理解
static final int hash(Object key) {
int h;
int temp;
if (key == null) {
return 0;
}else {
h = key.hashCode();
temp = h >>> 16;
return h ^ temp;
}
}
思路 :
- 判断key是否为空,如果为空就返回0值
- 否则获得一个key对应的hashCode值,这是一个32位的二进制数。但是由于后面进行索引确定的时候,
是使用 hash(key) & n -1(哈希值 & 数组容量-1),然后对于默认数组容量为16,n-1对应的二进制为后四位,其他位都为0,这时候与一个16位的哈希值相与,就是出现结果只取决于后四位的情况,这样不同的key所对应的索引更有可能是相同的,更容易发生哈希冲突。所以这里的hash函数采用h >>> 16 右移16位的操作,使得高16位的数据参与运算,增加索引的范围。 - 这里通过将高16位的数据与低16位的数据进行异或操作,这是因为相比与运算和或运算,异或运算获得0和1的概率都为50%。
4、(后续了解)
static Class<?> comparableClassFor(Object x) {
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; ParameterizedType p;
if ((c = x.getClass()) == String.