Java Set总结

711 阅读5分钟

Set是什么?

源码版本:JDK8 Set集合是Java集合框架的成员。是一个不包含重复元素最多只有一个空元素的集合抽象接口
该集合以equals()来判断两个元素是否相同,如果对象是可变类型,则必须特别小心对象的变动是否影响equals()的结果。 常用的主要实现类有:HashSetTreeSetLinkedHashSet image.png

HashSet

HashSet是什么?

HashSetSet集合基于哈希表的实现,该实现实际上是一个HashMap实例该实现允许一个空元素,不保证集合的遍历顺序。 基本特点:

  1. 允许一个空元素
  2. 无序
  3. 线程不安全
  4. 除非是迭代器自己的remove()方法,否则不允许在迭代的过程中增加、删除元素。

类图: image.png AbstractSet:提供Set接口的骨架实现,以最大限度地减少实现此接口所需的工作量。

HashSet的实现原理?

由构造函数可以看出,HashSet底层其实是一个HahaMap所以,理解**HashSet**的之前,需要先理解**HashMap**

private transient HashMap<E,Object> map;

 // 与Map中对象关联的虚拟值
private static final Object PRESENT = new Object();

public HashSet() {
    map = new HashMap<>();
}

public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
// 注意,最后这个构造函数,访问限定符并不是public,不能对外调用。只能在同一个包路径下调用,是专门给子类LinkedHashSet调用的。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

常用方法

添加元素是怎么实现的?

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

删除元素是怎么实现的?

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

TreeSet

TreeSet是什么?

TreeSetSet集合基于红黑树的实现,该实现实际上是一个TreeMap实例该集合是有序的。 基本特点:

  1. 是否允许空元素,则看情况而定。如果自定义的比较器支持空元组,则集合支持空元素
  2. 有序
  3. 线程不安全
  4. 除非是迭代器自己的remove()方法,否则不允许在迭代的过程中增加、删除元素。

类图: image.png SortedSet:提供元素整体排序的抽象类 NavigableSet:使用导航方法扩展SortedSet的抽象类,该类提供了给定目标附近元素的搜索防范。 AbstractSet:提供Set接口的骨架实现,以最大限度地减少实现此接口所需的工作量。

TreeSet的实现原理?

由构造函数可以看出,TreeSet的底层是一个TreeMap实力。

/**
 * The backing map.
 */
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;
}

public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}
public TreeSet(Collection<? extends E> c) {
    this();
    addAll(c);
}

public TreeSet(SortedSet<E> s) {
    this(s.comparator());
    addAll(s);
}

常用方法

添加元素是怎么实现的?

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

删除元素实现是怎么实现的?

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

导航方法

因为TreeMap实现了NavigableSet,所以可以使用一些导航方法。

descendingIterator
descendingSet
subSet
headSet
tailSet
subSet
headSet
tailSet
lower
floor
ceiling
higher
pollFirst
pollLast

LinkedHashSet

LinkedHashSet是什么?

LinkedHashSetSet集合基于散列表+双向链表的实现,该实现实际上是一个LinkedHashMap实例该集合是有序的。 基本特点:

  1. 允许一个空元素
  2. 有序,该集合维护了一个双向链表,集合的添加的顺序,就是其遍历顺序,如果元素已存在,重新添加,不会改变其在集合中的位置。
  3. 线程不安全
  4. 除非是迭代器自己的remove()方法,否则不允许在迭代的过程中增加、删除元素。

类图: image.png

实现原理

由源码可见,LinkedHashSet只是重写了构造方法,并没有实现重新实现任何接口。所以,对于HashSet使用的方法,对于LinkedHashSet同样适用。 由源码可见,LinkedHashSet使用父类HashSet的构造方法HashSet(int initialCapacity, float loadFactor, boolean dummy),该方法构架了一个LinkedHashMapLinkedHashSet就是基于LinkedHashMap实现。

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, 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);
    }

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

    /**
     * Constructs a new, empty linked hash set with the default initial
     * capacity (16) and load factor (0.75).
     */
    public LinkedHashSet() {
        super(16, .75f, true);
    }

    /**
     * Constructs a new linked hash set with the same elements as the
     * specified collection.  The linked hash set is created with an initial
     * capacity sufficient to hold the elements in the specified collection
     * and the default load factor (0.75).
     *
     * @param c  the collection whose elements are to be placed into
     *           this set
     * @throws NullPointerException if the specified collection is null
     */
    public LinkedHashSet(Collection<? extends E> c) {
        super(Math.max(2*c.size(), 11), .75f, true);
        addAll(c);
    }

    /**
     * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
     * and <em>fail-fast</em> {@code Spliterator} over the elements in this set.
     *
     * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
     * {@link Spliterator#DISTINCT}, and {@code ORDERED}.  Implementations
     * should document the reporting of additional characteristic values.
     *
     * @implNote
     * The implementation creates a
     * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator
     * from the set's {@code Iterator}.  The spliterator inherits the
     * <em>fail-fast</em> properties of the set's iterator.
     * The created {@code Spliterator} additionally reports
     * {@link Spliterator#SUBSIZED}.
     *
     * @return a {@code Spliterator} over the elements in this set
     * @since 1.8
     */
    @Override
    public Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
    }
}

几个常用实现各自的使用场景?

  1. HashSet使用HashMap实现,无序,允许空值。因为HashMap经过特俗优化,性能得到保障,所以HashSet的性能也能得到保障,如果没有特俗要求,建议使用该实现。
  2. TreeSet使用TreeMap实现,适用于需要特定顺序的场景。
  3. LinkedHashSet使用LinkedHashMap实现,适用于需要保证放入顺序的场景。

Set系列的集合以HashSet为核心,都是从Map结合扩展而来,如果理解了Map结合,Set集合也就理解了