刨析源码,深层讲解 Java-集合框架( 八 )


JDK8版本发布以后:是数组+链表+红黑树实现 。
3.2的底层实现原理
? jdk7:
map = new ():在实例化以后,底层创建了长度是16的一维数组Entry[] table 。
map.put(key1,):…可能已经执行过多次put…
首先,调用key1所在类的()计算key1哈希值,此哈希值经过某种算法计算以
后,得到在Entry数组中的存放位置 。
如果此位置上的数据为空,此时的key1-添加成功 。----情况1如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
2.1
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-添加成功 。-—情况2
2.2
如果key1的哈希值和已经存在的某一个数据key2-的哈希值相同,继续比较:调用key1所在类的(key2)方法,比较:
如果()返回false:此时key1-添加成功 。----情况3
如果()返回true:使用替换 。
补充:关于情况2和情况3:此时key1-和原来的数据以链表的方式存储 。
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容 。默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来 。
? jdk8 相较于jdk7在底层实现方面的不同:
new ():底层没有创建一个长度为16的数组 。
jdk 8底层的数组是:Node[],而非Entry[] 。
首次调用put()方法时,底层创建长度为16的数组 。
jdk7底层结构只有:数组+链表 。jdk8中底层结构:数组+链表+红黑树 。
形成链表时,七上八下(jdk7:新的元素指向旧的元素 。jdk8:旧的元素指向新的元素)
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储 。
3.3源码中的重要常量 常量描述
CITY
的默认容量,16
的最大支持容量,2^30
的默认加载因子
中链表长度大于该默认值,转化为红黑树
中红黑树存储的Node小于该默认值,转化为链表
桶中的Node被树化时最小的hash表容量 。(当桶中Node的数量达到需要变红黑树时,若hash表容量小于时,此时应执行扩容操作这个的值至少是的4倍 。)
table
存储元素的数组,总是2的n次幂
存储具体元素的集
size
中存储的键值对的数量
扩容和结构改变的次数 。
扩容的临界值,=容量*填充因子
填充因子
3.4 的存储结构:JDK 1.8之前
? 的内部存储结构其实是数组和链表的结合 。当实例化一个时,系统会创建一个长度为的Entry数组,这个长度在哈希表中被称为容量 (),在这个数组中可以存放元素的位置我们称之为“桶”(),每个都有自己的索引,系统可以根据索引快速的查找中的元素 。
? 每个中存储一个元素,即一个Entry对象,但每一个Entry对象可以带一个引
用变量,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Entry链 。而且新添加的元素作为链表的head 。
? 添加元素的过程:
向中添加(key,value),需要首先计算中key的哈希值(根据key所在类的()计算得到),此哈希值经过处理以后,得到在底层Entry[]数组中要存储的位置i 。如果位置i上没有元素,则直接添加成功 。如果位置i上已经存在(或还有链表存在的,),则需要通过循环的方法,依次比较中key和其他的entry 。如果彼此hash值不同,则直接添加成功 。如果
hash值不同,继续比较二者是否 。如果返回值为true,则使用的value
去替换为true的entry的value 。如果遍历一遍以后,发现所有的返回都