管中窥豹:HashSet源码学习

学习自:

http://www.cnblogs.com/skywang12345/p/3311252.html

      阅读HashSet的源码发现很有意思,四个`public修饰的构造函数内部全是new一个HashMap的实例,还有一个无修饰的构造函数new了一个LinkedHashMap,而LinkedHashMap又继承自HashMap。也就是说,HashSet是基于HashMap保存数据的。
      通读源码发现,内部实现的方法也比HashMap少了许多,而且很多方法其实就是在调用HashMap里的方法。

问题

1.下方代码中为何初始容量是从Math.max((int) (c.size()/.75f) + 1, 16)中选?c.size()/.75f) + 1代表什么?

1
2
3
4
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}

      首先,说明(c.size()/.75f) + 1

       因为从HashMap的效率(时间成本和空间成本)考虑,HashMap的加载因子是0.75。当HashMap的“阈值”(阈值 = HashMap总的大小 * 加载因子) < “HashMap实际大小”时,就需要将HashMap的容量翻倍。所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。

      接下来,说明为什么是 16 。
      因为HashMap的初始容量就是16,所以初始容量只能大于等于16且必须是2的幂。


2.既然HashSet的底层数据结构是HashMap,那么为什么不可以用put方法添加key-value对而只能用add方法添加值呢?
      首先,HashMap实现了Map接口,Map接口中定义了put方法而没有add方法;其次,HashSet实现的是Set接口,Set接口又继承自Collection接口,这两接口里只定义了add方法而没有put方法。
      虽然HashSet内部通过一个HashMap类型的map变量来保存数据,但是由于该map是私有的,外部无法访问,所以我也无法通过set.map.put(key, value)进行操作!!!
      HashSet所提供的add()内部其实是通过map.put()实现的:

1
2
3
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

      由add()方法可以得知,其实例如set.add(0)中的0是放进HashMap中的key里面的,而value则是常量PRESENT(Object的实例)。这也是为什么HashSet只提供了一个keySet集合而没有什么values等集合。


3.HashSet能插入重复值吗?
      不可以!前面说了,其实它插入值放在HashMap中的key里,key是不能重复的。


4.HashSet是有序的吗?
      不是!关键看add()方法就可以知道它插入数据的顺序了。底层其实还是调用的HashMapput方法。HashMap的索引由(n - 1) & hash]得到(长度和哈希值执行与运算),这样是无法保证数据间的顺序的。

Newer Post

管中窥豹:ArrayList源码学习

学习自:http://www.cnblogs.com/skywang12345/p/3308556.html removeAll和retainAll()看这里https://lizhongzhen11.github.io/2018/03/20/thinkingInJava/#1571 问题及注意点1 …

继续阅读
Older Post

管中窥豹:Hashtable源码学习

学习自:http://www.cnblogs.com/skywang12345/p/3310887.htmlhttps://segmentfault.com/a/1190000008982905 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;网上能搜到很多相关链接,加之平时 …

继续阅读