集合类源码(八)Collection之Set(HashSet, LinkedHashSet, TreeSet)
HashSet
先看一下成员变量
// 由此可见内部存储是一个HashMap
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() { map = new HashMap<>(); }add方法
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}仅仅是把新元素作为key,一个事先初始化好的空Object对象作为value,存入HashMap。
同理,contains方法和remove方法都是调用HashMap的方法
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}HashSet实现很简单,完全基于HashMap。
LinkedHashSet
先来看继承关系,继承了HashSet
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable本身也没有提供相关CRUD方法

来看下构造方法
public LinkedHashSet() {
// 调用的父类的方法
super(16, .75f, true);
}
// 父类(HashSet)构造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
// 和HashSet区别在于new了一个LinkedHashMap
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}这个构造方法是一个包私有的,仅仅提供给LinkedHashSet使用。
LinkedHashSet的实现基于LinkedHashMap
TreeSet
到这里,不用看也能知道,TreeSet的实现基于TreeMap....
来看成员变量
private transient NavigableMap<E,Object> m;
// 人家官方管这个Map中value的默认值叫做“哑值”
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}而add、contains、remove等方法也不必解释了
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
public boolean contains(Object o) {
return m.containsKey(o);
}
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}ConcurrentSkipListSet
虽然这个名字很长,人家也是线程安全的Set,但是,依然依托于Map的具体实现类
// 内部存储结构
private final ConcurrentNavigableMap<E,Object> m;
// 构造方法
public ConcurrentSkipListSet() {
m = new ConcurrentSkipListMap<E,Object>();
}
// add
public boolean add(E e) {
return m.putIfAbsent(e, Boolean.TRUE) == null;
}
// contains
public boolean contains(Object o) {
return m.containsKey(o);
}
// remove
public boolean remove(Object o) {
return m.remove(o, Boolean.TRUE);
}与前面不同的是,ConcurrentSkipListSet内部Map的value值永远是一个 Boolean.TRUE
优点:
1. 基于跳表实现,查找效率高
2. 线程安全
CopyOnWriteArraySet
这里呢,就不是基于Map的了,毕竟没有 CopyOnWriteArrayMap 这个集合类。虽然没有相关Map实现,但是是不是有一个 CopyOnWriteArrayList ?
private final CopyOnWriteArrayList<E> al;
/**
* Creates an empty set.
*/
public CopyOnWriteArraySet() {
al = new CopyOnWriteArrayList<E>();
}那是怎样去重的呢?
public boolean add(E e) {
// addIfAbsent:如果没有则添加,就是这样来保证无重复元素的
return al.addIfAbsent(e);
}其它都是直接调用其内部数据结构的方法
public boolean contains(Object o) {
return al.contains(o);
}
public boolean remove(Object o) {
return al.remove(o);
}总结:
Set旗下的集合一般都是站在Map集合的肩膀上的,懂了Map,就懂了Set
关于有序和无序:
0. 无序:HashSet
1. 按照自然顺序排序的:TreeSet、ConcurrentSkipListSet
2. 按照添加顺序排序的:LinkedHashSet、CopyOnWriteArraySet
测试程序
public static void main(String[] args) throws Exception {
deal(new HashSet<>());
deal(new LinkedHashSet<>());
deal(new TreeSet<>());
deal(new ConcurrentSkipListSet<>());
deal(new CopyOnWriteArraySet<>());
}
private static void deal(Set<String> set){
set.add("s1");
set.add("s9");
set.add("s3");
set.add("s5");
set.add("s7");
set.add("s2");
set.add("s8");
System.out.println(set.getClass().getName() + ":" + set);
}结果:
java.util.HashSet:[s3, s5, s7, s8, s9, s1, s2] java.util.LinkedHashSet:[s1, s9, s3, s5, s7, s2, s8] java.util.TreeSet:[s1, s2, s3, s5, s7, s8, s9] java.util.concurrent.ConcurrentSkipListSet:[s1, s2, s3, s5, s7, s8, s9] java.util.concurrent.CopyOnWriteArraySet:[s1, s9, s3, s5, s7, s2, s8]