Set集合的一些特性

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
关于有序和无序:

  • 无序: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]
下一篇
评论
说点什么吧?

发表评论

取消回复