集合类源码(八)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]