学习自:
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
代表什么?
首先,说明(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()
实现的:
由add()
方法可以得知,其实例如set.add(0)
中的0是放进HashMap
中的key
里面的,而value
则是常量PRESENT
(Object
的实例)。这也是为什么HashSet
只提供了一个keySet
集合而没有什么values
等集合。
3.HashSet
能插入重复值吗?
不可以!前面说了,其实它插入值放在HashMap
中的key
里,key
是不能重复的。
4.HashSet
是有序的吗?
不是!关键看add()
方法就可以知道它插入数据的顺序了。底层其实还是调用的HashMap
的put
方法。HashMap
的索引由(n - 1) & hash]
得到(长度和哈希值执行与运算),这样是无法保证数据间的顺序的。