Set 接口:HashSet & TreeSet & EnumSet & LinkedHashSet


首先需要知道 Set 的实现都是基于 Map 接口的实现类,因此在学习完 Map 接口后再来看 Set 就很明了。

HashSet

一些核心源码如下:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    // ... ...

}

可以明显的看到 HashSet 内部就是一个 HashMap,值为 HashMap 的 Key,而 Value 则是一个 final static 的 Object 常量。HashSet 有以下一些特点:

  • HashSet 中不能有相同的元素,可以有一个 Null 元素,存入的元素是无序的。
  • HashSet 保证唯一性的原理是 hashCode() 和 equals() 方法
  • 添加、删除操作时间复杂度都是O(1)
  • 非线程安全

LinkedHashSet

部分核心源码如下,从其调用的 HashSet 中的 super() 构造方法可以看出底层使用了 LinkedHashMap 结构。由此可知 LinkedHashSet 通过继承 HashSet,底层使用 LinkedHashMap,以很简单明了的方式来实现了其自身的所有功能。

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    }

    public LinkedHashSet() {
        super(16, .75f, true);
    }

}

// 上面调用的 HashSet 中的 super() 构造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

LinkedHashSet 有以下一些特点:

  • 没有重复元素,可以有一个 null 元素
  • 底层会维护一个双向链表,从而可以实现维持 insertion-order,并且不会因为某个元素被重复插入而改变
  • 链表保证了元素的有序即存储和取出一致,哈希表保证了元素的唯一性
  • 非线程安全
  • 添加、删除操作时间复杂度都是O(1)

TreeSet

从下面的部分源码可以看到 TreeSet 是基于 TreeMap(实现了 NavigableMap 接口) 实现的,默认是自然排序,也可以指定排序规则。它的特点如下:

  • TreeSet 中不能有相同元素,不可以有 Null 元素,根据元素的自然顺序进行排序。
  • TreeSet 底层通过红黑树(一种自平衡二叉查找树)结构实现排序
  • 添加、删除操作时间复杂度都是O(log(n))
  • 非线程安全
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    /**
     * Constructs a new, empty tree set, sorted according to the
     * natural ordering of its elements.  All elements inserted into
     * the set must implement the {@link Comparable} interface.
     */
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    /**
     * Constructs a new, empty tree set, sorted according to the specified
     * comparator. 
     *
     * @param comparator the comparator that will be used to order this set.
     *        If {@code null}, the {@linkplain Comparable natural
     *        ordering} of the elements will be used.
     */
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
}

线程安全

值得注意的是 Set 接口的这几个实现类都是非线程安全的,并且 JDK 并没有提供其对应的线程安全版本的实现,因此在需要同步的场景中需要调用 Collections.synchronized 方法实现,源码的注释上都有说明。

Set s = Collections.synchronizedSet(new HashSet(...));
Set s = Collections.synchronizedSet(new LinkedHashSet(...));
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

红黑树

红黑树是一种自平衡排序二叉树,树中每个节点的值,都大于或等于在它的左子树中的所有节点的值,并且小于或等于在它的右子树中的所有节点的值,这确保红黑树运行时可以快速地在树中查找和定位的所需节点。JDK 提供的集合类 TreeMap 本身就是一个红黑树的实现。我们将在 TreeMap 章节深入讨论一下红黑树。

Copyright © jverson.com 2019 all right reserved,powered by Gitbook 23:41:59

results matching ""

    No results matching ""