目录
一、前言
二、Hashtable详解
1.简介
2.特点
3.底层实现
4.HashMap VS Hashtable
三、Properties详解
1.简介
2.特点
3.具体使用(可以不看)
四、完结撒❀
一、前言
大家好,本篇博文是对Map接口常用实现类之一Hashtable类的源码分析,顺便讲一下它的子类Properties,考虑到Hashtable的使用频率,up不会像HashMap那样讲得很细致,但是底层的东西该说都会说的,比一般地方讲得还是要细点。
注意 : ①解读源码需要扎实的基础,比较适合希望深究的同学; ②不要眼高手低,看会了不代表你会了,自己能跟着过一遍才算有收获; ③点击文章的侧边栏目录或者前面的目录可以进行跳转。 良工不示人以朴,up所有文章都会适时改进。大家有问题都可以在评论区讨论交流,或者私信up。 感谢阅读!
二、Hashtable详解
1.简介
Hashtable是Map接口的一个实现类,地位上与HashMap平起平坐。Hashtable也属于java.base模块,java.util包下,如下图所示 :
我们再来看看Hashtable的类图,如下 :
2.特点
1° Hashtable中保存的也是key-value键值对,不过特别的是,Hashtable键值对中的"key"和"value"都不能为null,否则会抛出nullPointerException异常。
2° Hashtable中的一些常用方法在用法上基本和HashMap中的一致,比如put方法。
3° Hashtable中的成员方法用了synchronized关键字修饰,因此Hashtable是线程安全的,而HashMap是线程不安全的。
3.底层实现
0°
为了通过Debug来给大家分析Hashtable的源码,up以Hashtable_Demo类作为演示类来进行断点调试。Hashtable_Demo类代码如下 : (main函数第一行设置断点)
package csdn.knowledge.api_tools.gather.map;
import java.util.Hashtable;
/**
* @author : Cyan_RA9
* @version : 21.0
*/
public class Hashtable_Demo {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable();
hashtable.put(1, 141);
hashtable.put(2, 141);
hashtable.put(3, 141);
hashtable.put(4, 141);
hashtable.put(5, 141);
hashtable.put(6, 141);
hashtable.put(7, 141);
hashtable.put(8, 141);
hashtable.put(9, "这是集合第9个元素捏");
System.out.println(hashtable);
}
}
1°
Hashtable底层维护了Hashtable$Entry类型的数组table,用于存放键值对,如下图所示 :
通过查看Hashtable的源码能够找到这个Entry类型,如下图所示 :
可以看到,Hashtable$Entry其实就是Hashtable的一个静态内部类,并且同HashMap中的内部类HashMap$Node类一样,也实现了Map接口中的Entry内部接口。
2°
利用无参构造初始化Hashtable类对象时,会将底层的table数组初始化为长度等于11的数组。同样我们可以通过追溯Hashtable无参构造的源码来找到依据,如下 :
可以看到,Hashtable的无参构造,其实底层调用的是本类的一个有参构造,并且通过传入的实参我们可以得到以下信息——table数组的初始容量 = 11;加载因子 = 0.75。这俩就不用我多说了,都是老面孔了。
我们可以进入追进入这个有参构造中看看,如下图所示 :
别的不说,直接看最后两行,将new出的长度 = 11的Entry类型的数组赋值给了table数组,这条语句解释了table数组的初始容量为什么是11。
同时,threshold(临界值)被赋值为8。这个8是怎么来的?其实和我们之前讲得HashMap底层一样,threshold = table数组的长度 * 加载因子 = 11 * 0.75 = 8.25,因为此处进行了int类型的强制向下转型,所以最后赋值给threshold变量的是8,初始临界值 = 8。
关于"临界值有什么用"的问题up在之前的源码分析中讲过很多次了,这里只回顾一点——在HashMap底层中,当table数组中元素的个数超过临界值,就要对table数组进行扩容。
3°
对于Hashtable底层的table数组,其扩容时采用了"2n + 1"的机制。具体解释我们可以结合源码来看,如下图所示 :
先向table数组中加入8个元素,可以看到此时集合中键值对的个数count = 8,等于此时table数组的临界值threshold。此时,如果我们向集合中添加第9个元素,table数组就会进行扩容操作。我们可以通过Debug,来追一下添加第9个元素的put方法。首先我们进入put方法,如下图所示 :
在put方法中我们发现,真正完成添加元素的操作是在addEntry方法中进行的,因此,我们继续追入addEntry方法,如下图所示 :
观察addEntry方法的源码,我们可以清楚地看到Hashtable类和HashMap类在扩容时明显的两点不同——
①具体代码的执行顺序不同 :
HashMap底层是先完成添加元素的操作,再进行临界值的判断——如果当前元素加入集合后,会使得集合中元素的个数大于临界值threshold,就会跳入resize方法对table数组进行扩容。
而Hashtable则是先进行临界值的判断,再完成添加元素的操作——如果当前集合中元素的个数已经达到或超过当前的临界值,就会先跳入rehash方法对table数组进行扩容操作,等扩容成功后,再回来把当前键值对加入table数组中。
②对临界值的判断条件不同 :
HashMap底层对于临界值的判断条件是——如果当前集合中元素的个数大于当前临界值threshold,就进入resize方法对table数组进行扩容。
PS : 大家可能对HashMap底层的一些东西忘了,up将图放下面 :
而Hashtable对于临界值的判断条件却是——如果当前集合中元素的个数大于等于(达到或超过)当前临界值threshold,就进入rehash方法对table数组进行扩容。
当然,以上两点只是内在底层的一些不同,如果从外在宏观上来看,从结果上来看,其实这俩还是一样的
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)