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
优点:
- 基于跳表实现,查找效率高
- 线程安全
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
关于有序和无序:
- 无序:HashSet
- 按照自然顺序排序的:TreeSet、ConcurrentSkipListSet
- 按照添加顺序排序的: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]
发表评论
取消回复