HashSet
内部是hashMap或者LinkedHashMap(当为LinkedHashSet的时候)实现,看构造函数就知道
然而,问题来了,map是<key, value>,而set是只有key的,那么传入的value是什么,看看add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
传入了一个PRESENT,礼物?,在源码开头可以看到定义如下
private static final Object PRESENT = new Object();
而且,这里是static final变量,也就是说永远只有一个,利用了缓存原理,所以不会占用value的空间;这里的HashSet又给包装了一下,如果key没有重(oldValue==null),就返回true,否则返回false。(remove方法也有判断)
其余的方法都是调用hashmap做简单封装
TreeSet
HashSet是以HashMap为基础的,那么TreeSet当然也就以TreeMap为基础了,然而,发现其底层是一个NavigableMap接口类型的,这个接口实现了SortedMap,而TreeMap实现了NavigableMap这个接口,兜了一个圈子,发现
NavigableMap m = new TreeMap<>();
方法的包装跟HashSet差不多,操作Set里面的元素其实就是操作Map里面的元素
TreeSet是有序的,但并不是按照元素添加的顺序,而是按照里面的内容顺序
LinkedHashSet
这个更厉害了,以HashSet进行继承,进一步封装,底层是LinkedHashMap
LinkedHashMap的每一个键值对都是通过内部的静态类Entry<K, V>实例化的。这个Entry<K,V>类继承了HashMap.Entry类。这个静态类增加了两个成员变量,
before和after来维护LinkedHasMap元素的插入顺序。这两个成员变量分别指向前一个和后一个元素,这让LinkedHashMap也有类似双向链表的表现。
private static class Entry<K,V> extends HashMap.Entry<K,V>
{
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
}
而链表肯定不能缺少头结点,所以LinkedHashMap还定义了头结点
private transient Entry<K,V> header;