一.HashSet与HashMap的关系
阅读HashSet的源代码,发现加上很多注释总共就只有300多行,所以就可以猜到它的内部实现一定借助了其他的数据结构。事实上,它就是在HashMap的基础上实现的。在它的内部,包含了一个HashMap。
privatetransient HashMap<E,Object> map;
它的构造方法其实也是直接调用了HashMap的构造方法。
publicHashSet() {
map = new HashMap<>();
}
再来看一看它的add方法。
publicboolean add(E e) {
return map.put(e, PRESENT)==null;
}
其实就是调用了HashMap的put方法,key就是加入的e,value为PRESENT,它是HashSet内部的一个成员变量,就是一个空的Object对象。
privatestatic final Object PRESENT = new Object();
它是一个虚值,作用就是为了作为value与HashMap关联。add方法的返回值类型是boolean。通过上次分析的HashMap源码可以知道,当放入的key值在HashMap的table数组中不存在时,即没有hash冲突时,会把新元素加入数组中,同时返回null.所以这种情况下,add方法的返回值就是ture,表示进行了增加的操作。当放入的key,即HashSet要加入的e已经在HashMap的table数组中存在时,会用新的value覆盖旧的value,对于HashSet来说,就是用新的PRESENT替换了旧的PRESENT。然后直接返回旧的value值,并没有进行HashMap的插入操作。所以这个时候的add方法放入返回值就是false,表示HashSet没有大小以及元素的改变。
相似的,HashSet的remove方法也是直接调用了HashMap的remove方法
publicboolean remove(Object o) {
return map.remove(o)==PRESENT;
}
由于HashMap的remove方法当size为0或者要删除的元素不存在时,会返回null,所以这时add方法的返回值就是false,表示没有进行删除的操作,HashSet没有发生改变。当要删除的元素存在时,HashMap的remove方法会返回value的值,由于在HashSet中,所有的value都是PRESENT这一个对象,所以这个时候add的返回值就是true,表示HashSet进行了删除的操作,发生了改变。
二.Hashtable与HashMap的关系
首先发现一个比较有趣的细节问题,就是Hashtable的t没有大写,没有遵循java类命名的驼峰规则。不知道什么原因。
然后一个比较大的区别是他们的父类不同。
HashMap<K,V> extendsAbstractMap<K,V>
Hashtable<K,V> extendsDictionary<K,V>
他们的底层实现都是Entry数组,但是他们的初始默认的大小不一样,HashMap中默认的大小是16,而Hashtable的默认大小确是11,但是他们的默认负载因子都是0.75.下面为他的无参的构造方法:
public Hashtable() {
this(11, 0.75f);
}
然后他们的求数组下标的哈希映射的算法实现不一样,HashMap使用的是&运算,效率相对要高一些。而Hashtable使用的是取模的余数运算。
int index = (hash & 0x7FFFFFFF) % tab.length;
他们最大的区别就是Hashtable中所有的可能导致线程安全问题的方法都使用了synchronized修饰,加了互斥锁。所以说Hashtable是线程安全的。但是缺点就是效率很低。他们的区别就好像Vector和ArrayList。他们还有很多的细节上的区别,大家无聊的时候可以去看看。