一、前言
在上篇《Java 集合框架之Collection源码解析》中,主要对集合层次结构中的根接口Collection进行源码分析,本章将针对其子接口Set系列展开分析。
二、Set
Set是继承Collection接口的不包含重复元素的集合。
public interface Set<E> extends Collection<E> {
...
}
我们来看看Set接口中定义了哪些方法
package java.util;
/*
* @see Collection
* @see List
* @see SortedSet
* @see HashSet
* @see TreeSet
* @see AbstractSet
* @see Collections#singleton(java.lang.Object)
* @see Collections#EMPTY_SET
* @since 1.2
*/
public interface Set<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean retainAll(Collection<?> c);
boolean removeAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT);
}
}
基本上都是继承Collection接口的方法,但是在spliterator这个方法中返回的Spliterator指定特征值为DISTINCT,也就说明Set集合的元素都是不可重复。
大家可以注意到,在接口的注释中提到了SortSet,HashSet,TreeSet,AbstractSet,其中SortSet为接口,其余三个均为集合类。
再来看看官网文档中对Set的描述
Interface Set<E>
Type Parameters:
E - the type of elements maintained by this set
All Superinterfaces:
Collection<E>, Iterable<E>
All Known Subinterfaces:
NavigableSet<E>, SortedSet<E>
All Known Implementing Classes:
AbstractSet, ConcurrentHashMap.KeySetView, ConcurrentSkipListSet, CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet
Set继承的父接口有Collection和Iterable,继承Set的子接口有NavigableSet和SortedSet。
三、SortedSet
SortedSet中的元素无序不可重复且存进去的元素可以按照元素大小顺序自动排序,
接下来我们来看看SortSet接口有哪些方法,它又是如何实现自动排序的。
public interface SortedSet<E> extends Set<E> {
//返回用于对该集合中的元素排序的比较器
Comparator<? super E> comparator();
//返回该集合指定元素范围的SortedSet
SortedSet<E> subSet(E fromElement, E toElement);
//返回从头至指定位置的SortedSet
SortedSet<E> headSet(E toElement);
//返回从指定位置开始的SortedSet
SortedSet<E> tailSet(E fromElement);
//返回当前集合的第一个元素
E first();
//返回当前集合的最后一个元素
E last();
//返回不重复的有序可分割迭代器
@Override
default Spliterator<E> spliterator() {
return new Spliterators.IteratorSpliterator<E>(
this, Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED) {
@Override
public Comparator<? super E> getComparator() {
return SortedSet.this.comparator();
}
};
}
}
不难发现,在spliterator()的方法实现中,返回的Spliterator满足DISTINCT 、SORTED 、ORDERED三个特征值,spliterator的比较器必须与排序集的比较器相同,或强制执行与排序集的比较器相同的总排序。这也就是SortSet能够自动排序的原因。
那Comparator比较器又是怎样定义的
Comparator是比较接口,如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序。
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
...
}
若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2) 函数。
Comparator其他方法在此就不做介绍了,有兴趣的小伙伴可以自己去看看Comparator接口的源码
Comparable接口
Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
public interface Comparable<T> {
//一个负整数,零,或正整数作为这个对象 小于、等于或大于指定对象的返回值
public int compareTo(T o);
}
Comparable接口只有一个compareTo()方法.
在Comparable源码注释中有一段解释:
强烈推荐,但不是严格要求(x.compareTo(y)==0) == (x.equals(y))。
一般来说,任何实现Comparable接口并违反此条件的类都应该清楚地表明这一事实。推荐使用的语言是“注意:该类的自然顺序与equals不一致。”
也就是说compareTo()这个方法不完全等价于equals(),可以有自定义的返回值。
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
官方文档对SortSet接口的结构介绍
Interface SortedSet<E>
Type Parameters:
E - the type of elements maintained by this set
All Superinterfaces:
Collection<E>, Iterable<E>, Set<E>
All Known Subinterfaces:
NavigableSet<E>
All Known Implementing Classes:
ConcurrentSkipListSet, TreeSet
有两个实现类分别是ConcurrentSkipListSet和TreeSet
四、TreeSet
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable{
...
}
从上面TreeSet的继承关系和接口实现上可以发现,TreeSet继承了AbstractSet抽象类,实现了NavigableSet, Cloneable, java.io.Serializable接口,
TreeSet实现了NavigableSet(SortedSet的扩展,使用导航方法报告给定搜索目标的最接近匹配),意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
TreeSet 实现了Cloneable接口,意味着它能被克隆。
TreeSet 实现了java.io.Serializable接口,意味着它支持序列化。
TreeSet的继承关系如下:
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.TreeSet<E>
还是先来看看TreeSet类中定义了哪些成员变量和方法
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/**
* 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
*/
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
/**
* 构造一个新的空TreeSet,根据其元素的自然顺序进行排序.
*/
public TreeSet() {
this(new TreeMap<E,Object>());
}
/**
* 构造一个新的空TreeSet,根据指定的比较器进行排序.
*/
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
/**
* 构造TreeSet,其中包含指定集合中的元素,并根据其元素的自然顺序进行排序
*/
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 构造包含相同元素的TreeSet,并使用与指定排序集相同的顺序.
*/
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
/**
* 按升序返回此集合中元素的迭代器.
*/
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
/**
* 返回按降序遍历此集合中的元素的迭代器.
* @since 1.6
*/
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
}
/**
* @since 1.6
*/
public NavigableSet<E> descendingSet() {
return new TreeSet<>(m.descendingMap());
}
/**
* 返回该集合中的元素数量.
*/
public int size() {
return m.size();
}
/**
* 如果该集合不包含元素,则返回true.
*/
public boolean isEmpty() {
return m.isEmpty();
}
/**
* 如果该集合包含指定的元素,则返回true.
*/
public boolean contains(Object o) {
return m.containsKey(o);
}
/**
* 如果指定的元素尚未出现,则将其添加到此集合.
*/
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
/**
* 如果指定元素存在,则从该集合中移除该元素.
*/
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
/**
* 从这个集合中删除所有元素.
* 调用返回后,该集合将为空.
*/
public void clear() {
m.clear();
}
/**
* 将指定集合中的所有元素添加到此集合
*/
public boolean addAll(Collection<? extends E> c) {
// Use linear-time version if applicable
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
return super.addAll(c);
}
/**
* 返回指定范围的NavigableSet
* @since 1.6
*/
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
E toElement, boolean toInclusive) {
return new TreeSet<>(m.subMap(fromElement, fromInclusive,
toElement, toInclusive));
}
/**
* @since 1.6
*/
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new TreeSet<>(m.headMap(toElement, inclusive));
}
/**
* @since 1.6
*/
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new TreeSet<>(m.tailMap(fromElement, inclusive));
}
/**
* 返回指定范围的SortedSet
*/
public SortedSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
}
public SortedSet<E> headSet(E toElement) {
return headSet(toElement, false);
}
public SortedSet<E> tailSet(E fromElement) {
return tailSet(fromElement, true);
}
public Comparator<? super E> comparator() {
return m.comparator();
}
/**
* 返回第一个元素
*/
public E first() {
return m.firstKey();
}
/**
* 返回最后一个元素
*/
public E last() {
return m.lastKey();
}
// NavigableSet API methods 支持一系列的导航方法
/**
* @since 1.6
*/
public E lower(E e) {
return m.lowerKey(e);
}
/**
* @since 1.6
*/
public E floor(E e) {
return m.floorKey(e);
}
/**
* @since 1.6
*/
public E ceiling(E e) {
return m.ceilingKey(e);
}
/**
* @since 1.6
*/
public E higher(E e) {
return m.higherKey(e);
}
/**
* @since 1.6
*/
public E pollFirst() {
Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
/**
* @since 1.6
*/
public E pollLast() {
Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
//支持克隆
@SuppressWarnings("unchecked")
public Object clone() {
TreeSet<E> clone;
try {
clone = (TreeSet<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
clone.m = new TreeMap<>(m);
return clone;
}
//支持序列化
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden stuff
s.defaultWriteObject();
// Write out Comparator
s.writeObject(m.comparator());
// Write out size
s.writeInt(m.size());
// Write out all elements in the proper order.
for (E e : m.keySet())
s.writeObject(e);
}
//支持反序列化
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden stuff
s.defaultReadObject();
// Read in Comparator
@SuppressWarnings("unchecked")
Comparator<? super E> c = (Comparator<? super E>) s.readObject();
// Create backing TreeMap
TreeMap<E,Object> tm = new TreeMap<>(c);
m = tm;
// Read in size
int size = s.readInt();
tm.readTreeSet(size, s, PRESENT);
}
public Spliterator<E> spliterator() {
return TreeMap.keySpliteratorFor(m);
}
private static final long serialVersionUID = -2479143000061671589L;
}
TreeSet中含有一个NavigableMap类型的成员变量m,而m实际上是TreeMap的实例,底层数据结构是二叉树。关于Map系列的源码解析可以查看《Java集合之Map系列源码解析》
好了,说完TreeSet,我们再来看HashSet。
五、HashSet
官方文档的对HashSet的定义如下:
这个类实现Set接口,由一个散列表(实际上是一个HashMap实例)支持。不保证集合的迭代顺序;特别是,它不能保证顺序随时间保持不变。该类允许使用null元素。这个类为基本操作(添加、删除、包含和大小)提供了恒定的时间性能,前提是哈希函数将元素适当地分散到桶中。遍历这个集合需要的时间与HashSet实例的大小(元素的数量)和加上支持HashMap实例的“容量”(桶的数量)成比例。因此,如果迭代性能很重要,那么不要将初始容量设置得太高(或者负载系数设置得太低)。请注意,此实现不是同步的。如果多个线程同时访问一个散列集,并且其中至少有一个线程修改了该集,则必须在外部同步该散列集。这通常是通过同步一些自然封装了集合的对象来实现的
我们还是先看看源码,再来理解上面的描述吧。
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
//HashSet就是通过HashMap保存数据, HashSet的值就是HashMap的key
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
//HashMap 为<key, value>的键值对, 既然HashSet的值就是HashMap的key, 那么HashMap的值就是这个PRESENT
private static final Object PRESENT = new Object();
//下面这一系列的构造方法都是创建HashMap
public HashSet() {
map = new HashMap<>();
}
//指定HashMap的初始容量和负载因子
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
//指定HashMap的初始容量
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
//构造一个内部为LinkedHashMap的HashSet,dummy参数只是用来区分其他构造函数
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
//如果没有指定HashMap的capacity容量, 那么默认的就是16
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
//返回当前集合的迭代器
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public void clear() {
map.clear();
}
/**
* 支持克隆
*/
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
/**
* 支持序列化
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 写出任何隐藏的序列化
s.defaultWriteObject();
// 写出HashMap容量和负载因子
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// Write out size
s.writeInt(map.size());
// 按顺序写出所有的元素。.
for (E e : map.keySet())
s.writeObject(e);
}
/**
* 支持反序列化
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 读取任何隐藏的序列化
s.defaultReadObject();
// 读取容量并验证非负。
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// 读取负载因子,验证为正非空。
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// 读取大小并验证非负
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
SharedSecrets.getJavaOISAccess()
.checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// 按顺序读取所有元素
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
/**
* @since 1.8
*/
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
}
根据上面的源码分析,我们可以得出以下几点
HashSet基于HashMap实现, 以HashSet的值作为HashMap的一个key, 以一个Object对象常量作为HashMap的值。
HashSet允许拥有1个为null的值, HashSet的值不可重复。
HashSet不能重复存储equals相同的数据 。原因就是equals相同,数据的散列码也就相同(equals重写,hashCode必须重写)
总结:
TreeSet和HashSet对比
TreeSet | HashSet | |
---|---|---|
与Set接口关系 | 实现类 | 实现类 |
是否同步 | false | false |
是否支持排序 | true | false |
是否支持clone | true | true |
元素允许为null | false | true |
实现原理 | 二叉树 | 哈希表 |