//节点的颜色
boolean red;
TreeNode(int hash, K key, V val, Node next) {
super(hash, key, val, next);
}
}
//这个静态类是 LinkedHashMap.Entry类,可以发现它是继承于HashMap的Node类,也就是所TreeNode是Node的子类。
static class Entry extends HashMap.Node {
}
由代码就可以得出TreeNode是Node的子类,这也是为什么图中树节点还有指向其他节点的原因,既然HashMap有把单链表转为红黑树,那就有红黑树转为单链表,而采用这种结构
为后面节点红黑树节点少于6的时候需要进行转为单链表的过程加快了转换的速度。
HashMap的数据结构就分析到这里,下面来开始分析HashMap的源码:
全局变量及其作用分析
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable {
//默认初始化容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量是2^30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认负载因子是0.75 //大部分容器的负载因子都是0.75应该是经过很久的择优选择
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//默认阈值为8 ,链表的节点数超过阈值就需要转换为红黑树
static final int TREEIFY_THRESHOLD = 8;
//默认转换为单链表的阈值为6,即如果红黑树的节点少于6的时候会自动转为单链表
static final int UNTREEIFY_THRESHOLD = 6;
//树形化的的最小容量为64,如果阈值超过8但哈希表容量值小于64一样不会转换为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
//存储Node类型的节点及其子类的数组(哈希表)
transient Node[] table;
//数据大小
transient int size;
//操作修改的次数
transient int modCount;
//k-v对数量的最大值 方后续扩容又要重新计算哈希值,所以2的幂次方会起到一定优化性能的作用,因为以2的幂次方扩容后,旧数据的位置要么在原来的位置,要么在原理的位置基
础上移动+2的幂次方
这个变量的计算公式: 负载因子*容量
int threshold;
//负载因子
final float loadFactor;
}
由上面分析可以知道,HashMap一开始是数组+链表的方式存储,当阈值超过8和哈希表的容量达到最小树形化容量64的时候才会把该桶(链表)转换为红黑树结构,当红黑树节点低于
6的时候由将红黑树切割成链表。
哈希码的计算与位置的关系
先观察一下源码
static final int hash(Object key) {
int h;
//key的哈希码与它的高16进行异或运算(低16位与高16位运算)
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//n是哈希表的容量 下面这点代码是添加元素方法里面抽取出来的,可以看到是根据 n-1和hash进行与运算得出具体位置,只有容量是2的幂次方,计算的总是旧数组的位置或者旧数组的位置+旧容量
tab[index = (n - 1) & hash]
HashMap中中haxsh(Object key)方法是根据(方法是根据(key的哈希码)与(的哈希码)与(key的哈希码的高的哈希码的高16位)进行(异或运算)得到新的哈希码,但是想要确定该节点应该存储在哈希表中的那个位置还位)进行(异或运算)得到新的哈希码,但是想要确定该节点应该存储在哈希表中的那个位置还
需要(新的哈希码)与(哈希表的容量需要(新的哈希码)与(哈希表的容量-1)进行(与运算))进行(与运算)
简单的来说元素应该存储在那个桶(那条链表或者红黑树)与key的哈希码和哈希表的容量有关
下图是大体过程
3. HashMap的扩容机制
/**
评论0
最新资源