Java 集合 HashSet VS LinkedHashSet VS TreeSet

更多 Java 集合类方面的文章,请参见文集《Java 集合类》


共同点:

  • 都不能包含重复元素,用 equals 方法判断
  • 都是线程不安全的
  • Fail-fast behavior

基本区别:

  • 迭代顺序:

    • HashSet :遍历时没有特定的顺序,从前到后遍历链表散列(数组 + 链表)
    • LinkedHashSet :遍历时按照插入的顺序或者访问的顺序。
    • TreeSet :遍历时按照元素升序(通过元素的 compareTo 方法进行比较)。
  • put/remove/get 时间复杂度

    • HashSet :O(1)
    • LinkedHashSet :O(1)
    • TreeSet :O(lgN)
  • 是否允许空的元素

    • HashSet :允许空的元素,因为 HashSet 基于 HashMap 实现,HashMap 允许空的 key
    • LinkedHashSet :允许空的元素,因为 LinkedHashSet 基于 LinkedHashMap 实现,LinkedHashMap 允许空的 key
    • TreeSet :元素不能为空,因为 TreeSet 基于 TreeMap 实现,HashMap 不允许空的 key,否则会产生运行时异常。
  • 实现方式

    • HashSet :基于 HashMap
    • LinkedHashSet :基于 LinkedHashMap
    • TreeSet :基于 TreeMap

HashSet 的实现原理

构造

public class HashSet
    extends AbstractSet
    implements Set, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap 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 HashMap instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

可以看出,对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素。
因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,我们应该为保存到 HashSet 中的对象覆盖 hashCode() 和 equals()。

添加元素

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

要添加的元素 e 作为 key,构造一个键值对 ,然后调用 HashMap 的 put 方法。(PRESENT 为一个对象,在上面的构造方法中可以看出)
如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2)) 的元素 e2,则向此 set 添加指定的元素 e。如果此 set 已包含该元素,则该调用不更改 set 并返回 false。但底层实际将将该元素作为 key 放入 HashMap。

LinkedHashSet 的实现原理

构造

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

    private static final long serialVersionUID = -2851667679971038690L;

    /**
     * Constructs a new, empty linked hash set with the specified initial
     * capacity and load factor.
     *
     * @param      initialCapacity the initial capacity of the linked hash set
     * @param      loadFactor      the load factor of the linked hash set
     * @throws     IllegalArgumentException  if the initial capacity is less
     *               than zero, or if the load factor is nonpositive
     */
    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

可以看出:

  • LinkedHashMap 继承了 HashMap
  • LinkedHashSet 继承了 HashSet,实际上是创建了一个 LinkedHashMap:
    在 LinkedHashSet 构造方法中调用了父类 HashSet 的构造方法super(initialCapacity, loadFactor, true);
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

因此 LinkedHashSet 的实现原理可以参考 LinkedHashMap的实现原理

TreeSet 的实现原理

构造

可以看出 TreeSet 在构造方法中实际上创建了一个 TreeMap。

public class TreeSet extends AbstractSet
    implements NavigableSet, Cloneable, java.io.Serializable
{
    /**
     * The backing map.
     */
    private transient NavigableMap m;

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

    /**
     * Constructs a set backed by the specified navigable map.
     */
    TreeSet(NavigableMap 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.
     * Furthermore, all such elements must be mutually
     * comparable: {@code e1.compareTo(e2)} must not throw a
     * {@code ClassCastException} for any elements {@code e1} and
     * {@code e2} in the set.  If the user attempts to add an element
     * to the set that violates this constraint (for example, the user
     * attempts to add a string element to a set whose elements are
     * integers), the {@code add} call will throw a
     * {@code ClassCastException}.
     */
    public TreeSet() {
        this(new TreeMap());
    }

引用:
HashSet 的实现原理
LinkedHashSet 的实现原理

你可能感兴趣的:(Java 集合 HashSet VS LinkedHashSet VS TreeSet)