Set是什么?
源码版本:JDK8
Set集合是Java集合框架的成员。是一个不包含重复元素,最多只有一个空元素的集合抽象接口。
该集合以equals()来判断两个元素是否相同,如果对象是可变类型,则必须特别小心对象的变动是否影响equals()的结果。 常用的主要实现类有:HashSet、TreeSet、LinkedHashSet
HashSet
HashSet是什么?
HashSet是Set集合基于哈希表的实现,该实现实际上是一个HashMap实例,该实现允许一个空元素,不保证集合的遍历顺序。
基本特点:
- 允许一个空元素
- 无序
- 线程不安全
- 除非是迭代器自己的
remove()方法,否则不允许在迭代的过程中增加、删除元素。
类图:
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是什么?
TreeSet是Set集合基于红黑树的实现,该实现实际上是一个TreeMap实例,该集合是有序的。
基本特点:
- 是否允许空元素,则看情况而定。如果自定义的比较器支持空元组,则集合支持空元素
- 有序
- 线程不安全
- 除非是迭代器自己的
remove()方法,否则不允许在迭代的过程中增加、删除元素。
类图:
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是什么?
LinkedHashSet是Set集合基于散列表+双向链表的实现,该实现实际上是一个LinkedHashMap实例,该集合是有序的。
基本特点:
- 允许一个空元素
- 有序,该集合维护了一个双向链表,集合的添加的顺序,就是其遍历顺序,如果元素已存在,重新添加,不会改变其在集合中的位置。
- 线程不安全
- 除非是迭代器自己的
remove()方法,否则不允许在迭代的过程中增加、删除元素。
类图:
实现原理
由源码可见,LinkedHashSet只是重写了构造方法,并没有实现重新实现任何接口。所以,对于HashSet使用的方法,对于LinkedHashSet同样适用。
由源码可见,LinkedHashSet使用父类HashSet的构造方法HashSet(int initialCapacity, float loadFactor, boolean dummy),该方法构架了一个LinkedHashMap。LinkedHashSet就是基于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);
}
}
几个常用实现各自的使用场景?
HashSet使用HashMap实现,无序,允许空值。因为HashMap经过特俗优化,性能得到保障,所以HashSet的性能也能得到保障,如果没有特俗要求,建议使用该实现。TreeSet使用TreeMap实现,适用于需要特定顺序的场景。LinkedHashSet使用LinkedHashMap实现,适用于需要保证放入顺序的场景。
Set系列的集合以HashSet为核心,都是从Map结合扩展而来,如果理解了Map结合,Set集合也就理解了