HashSet详解
List和Set都实现自Collection,List保证元素的添加顺序,元素可重复。而Set不保证元素的添加顺序,元素不可重复。
1 | Set不保证插入有序是指Set这个接口的规范,实现类只要遵循这个规范即可,当然也可以写有序的版本出来,比如LinkedHashSet。 |
Set接口继承自Collection。有两个很重要的实现HashSet和TreeSet。
执行如下代码:
1 | public static void main(String[] args){ |
第一行代码new了一个HashSet,在堆内存里开辟了一块空间,先找到HashSet的构造函数看看:
1 | public HashSet() { |
再看一下map:
1 | private transient HashMap<E, Object> map; |
就是一个HashMap,如下图:
继续执行代码,往strSet添加元素”张三”,再看add方法:
1 | public boolean add(E e) { |
原来就是调用底层HashMap的put方法,把”张三”作为key,PRESENT作为value放在hashMap里,如果put时key重了,会返回被覆盖的value值(oldValue),否则返回null,这儿的HashSet又给包装了一下,如果key没有重(oldValue == null),就返回true,否则返回false。继续看PRESENT:
1 | private static final Object PRESENT = new Object(); |
很简单就是new了一个Object,如下图:
调用底层HashMap的时候,key是传进去的“张三”,value是PRESENT,也就是一个Object对象,继续往里依次放入“李四”、“王五”、“赵六”,value都是一样的,为PRESENT,如下图:
所有元素的value都指向Object对象,HashSet虽然底层是用HashMap来实现的,但由于用不到HashMap的value,所以不会为底层HashMap的每个value分配一个内存空间,因此并不会过多的占用内存,请放心使用。
再来看看示例代码里的size()、isEmpty()、remove()、contains()、clear()等方法的实现,调用的是底层HashMap的相关方法:
1 | public int size() { |
这些方法基本上没什么逻辑代码,就是复用了HashMap里的方法而已。HashSet就是利用HashMap来实现的。同样,TreeSet也是用TreeMap来实现的。
HashSet底层声明了一个HashMap,HashSet做了一层包装,操作HashSet里的元素时其实是在操作HashMap里的元素。TreeSet底层也是声明了一个TreeMap,操作TreeSet里的元素其实是操作TreeMap里的元素。
参考资料:
清浅池塘 HashSet的秘密