从AbstractSet继承来的子类有HashSet、LinkedHashSet、TreeSet等。
HashSet:
持有一个HashMap引用,使用它的Entry来保存对象(key域指向我们真正保存的对象,value域指向同一个静态finalObject对象),因此HashSet元素不能重复,可以有一个null。不保证元素顺序(因为HashMap不保证元素顺序,且table数组会自动扩容,扩容时会以lo、hi算法移动链表上部分节点的存储位置)。使用的迭代器就是HashMap中成员内部类keySet使用的KeyIterator迭代器。非线程安全。
LinkedHashSet:
由于继承自HashSet(且并未覆盖任何方法),所以基本继承了HashSet所有特性,只是持有的是LinkedHashMap的引用(数组保存每一条链表之外,节点还有before和after两个域将节点按照插入顺序构成双向链表),所以可以保证元素顺序。同样由于使用Entry的key存储对象,所以不重复,可以有一个null。非线程安全。
TreeSet:
持有一个TreeMap的引用,使用他的Entry来保存对象(key域指向我们真正保存的对象,value域指向同一个静态finalObject对象),因为TreeMap的对象比较依赖于key的比较器,所以我们要保存的对象不能为null、且必须实现Comparable接口。由于TreeMap不保证插入顺序(因为红黑树的旋转和修建),但是保证某一个时刻所有元素的大小顺序(比较器指定比较方式),可以导出key、value、Entry的有序集合,所以TreeSet可以保持元素的大小顺序(比较器指定的排序方式)。非线程安全。
1.HashSet
HashSet比较简单。首先HashSet从AbstractSet继承而来,相关实现接口源码如下:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { }
他的所有操作都是用HashMap对象实现的,保存到HashSet的对象实际上是保存为HashMap中节点Entry的key。所以HashSet元素不可重复、可以为null(null键在HashMap的table[0]位置)。成员申明如下:
//保存节点数据 //key域指向我们真正保存的对象 //value域指向同一个静态不可变对象 //HashSet保存对象和HashMap保存key的要求一样:不能重复,可以为null private transient HashMap<E,Object> map; //所有键值对的value private static final Object PRESENT = new Object();
他的构造器其实也是基于HashMap的几个构造器,值得注意的是最后一个构造器会创建LinkedHashMap对象而不是HashMap对象。这个构造器是给LinkedHashSet使用的。其源码如下:
//构造器 //默认容量16加载因子0.75创建HashMap public HashSet() { map = new HashMap<>(); } //指定容量创建HashMap public HashSet(Collection<? extends E> c) { //计算容量,这个容量保证集合c所有元素都插入table数组中也不会达到扩用阈值 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); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
需要保存的对象的所有操作都是基于HashMap对象实现的,源码如下:
//由于我们保存的对象实际上是key //所以直接使用HashMap的公共导出方法keySet()取得key的Set集合 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(); }
剩下的就是一些Object方法,源码如下:
//Object方法 @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(); s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); 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); 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); } } public Spliterator<E> spliterator() { return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); }
2.LinkedHashSet
LinkedHashSet相比更加简单,继承自HashMap,基于LinkedHashMap实现的整个类文件短短40行代码。类申明源码如下:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, java.io.Serializable { }
类文件仅仅包含几个构造器,调用的HashSet的构造器,创建一个LinkedHashMap对象,从父类继承的map成员引用这个新创建的对象。源码如下:
//构造器 //底层均是调用HashSet的构造器HashSet(initialCapacity,loadFactor,dummy) //会创建一个LinkedHashMap对象 //从父类继承的成员map指向上述对象 public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } public LinkedHashSet() { super(16, .75f, true); } public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } @Override public Spliterator<E> spliterator() { return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED); }
除此之外并未覆盖任何父类方法,但是他是怎么支持链表操作的呢,可以查看其父类HashSet方法调用链,源码如下:
//HashSet的add(e) public boolean add(E e) { return map.put(e, PRESENT)==null; } //HashMap的put(key,value) public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } //HashMap的putVAl(hash,key,value,onlyIfAbsent,evict) final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) { //省略 //这个方法记录了该节点在被操作之后(比如添加到map)需要执行的操作 afterNodeAccess(e); // afterNodeInsertion(evict); return null; } //HashMap的afterNodeAccess(e) void afterNodeInsertion(boolean evict) { //HashMap里没有任何操作 } //LinkedHashMap的afterNodeAccess(e) void afterNodeAccess(Node<K,V> e) { //省略 //这里将被访问的节点添加到了双链表的尾部 }
可以看到,底层基于LinkedHashMap来调用从父类继承的方法,父类HashMap的基础方法实现中已经考虑到了后续子类的扩展,于是利用方法自用性,调用了一个不做任何处理的函数。如果是HashMap对象来调用基础方法,其方法内部调用的扩展方法不做任何处理,如果是LinkedHashMap对象来调用基础方法,就可以自己指定如何进行扩展。我们再看迭代器实现,源码如下:
//LinkedHashSet没有覆盖HashSet的迭代器方法,HashSet的iterator()方法如下: public Iterator<E> iterator() { return map.keySet().iterator(); } //LinkedHashMap的keySet()方法如下: public Set<K> keySet() { Set<K> ks; //LinkedKeySet是LinkedHashMap成员内部类实现 return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks; } //LinkedKeySet成员内部类如下: final class LinkedKeySet extends AbstractSet<K> { //省略 public final Iterator<K> iterator() { //LinkedKeyIterator是迭代器的内部类实现 return new LinkedKeyIterator(); } } //LinkedKeyIterator成员内部类如下: final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K> { public final K next() { //next返回的是当前节点的after域指向的节点,即使用双链表的方式迭代 return nextNode().getKey(); } }
我们可以看到,虽然并没有覆盖父类的方法,但是利用父类引用子类对象,使用运行时动态绑定调用子类方法,利用继承的层级关系和多态很方便的实现了LinkedHashSet的全部功能。
3.TreeSet
TreeSet持有一个TreeMap对象的引用,所有操作都基于TreeMap来实现。类文件代码仅仅200多行。首先查看他的接口 NavigableSet<E>,这个接口继承了SortedSet<E>接口以提供更详细的有序集合的相关操作。源码如下:
public interface NavigableSet<E> extends SortedSet<E> { //返回的是集合中比参数元素更小的所有元素中最大的那个 E lower(E e); //返回的是集合中小于等于参数元素的所有元素中最大的那个 E floor(E e); //返回的是集合中大于等于参数元素的所有元素中最小的那个 E ceiling(E e); //返回的是集合中大于参数元素E pollLast();的所有元素中最小的那个 E higher(E e); //将第一个元素踢出并返回 E pollFirst(); //将最后一个元素踢出并返回 E pollLast(); Iterator<E> iterator(); //得到一个倒序排列的本集合实例 NavigableSet<E> descendingSet(); //返回的是倒序迭代器 Iterator<E> descendingIterator(); //将本集合截取的方法,两个boolean参数表示是否包含被截取的头元素和尾元素。 NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive); //返回的是参数元素之前的元素集合,boolean参数表示参数元素是否被包含。 NavigableSet<E> headSet(E toElement, boolean inclusive); //返回的是参数元素之后的元素集合,boolean参数表示参数元素是否被包含。 NavigableSet<E> tailSet(E fromElement, boolean inclusive); //截取集合 SortedSet<E> subSet(E fromElement, E toElement); //截取集合 SortedSet<E> headSet(E toElement); //截取集合 SortedSet<E> tailSet(E fromElement); }
查看TreeSet类申明,源码如下:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable { }
查看其成员、构造器。可以看到包含一个TreeMap对象的引用,他的所有构造器其实都是对TreeMap的不同构造器的封装。源码如下:
//NavigableMap<K,V>子类,引用一个TreeMap private transient NavigableMap<E,Object> m; //TreeMap中所有value均使用这个对象 private static final Object PRESENT = new Object(); //构造器 TreeSet(NavigableMap<E,Object> m) { //指定TreeMap创建TreeSet this.m = m; } public TreeSet() { //自然顺序创建TreeMap,再创建TreeSet this(new TreeMap<E,Object>()); } public TreeSet(Comparator<? super E> comparator) { //指定排序方式创建TreeMap,再创建TreeSet this(new TreeMap<>(comparator)); } public TreeSet(Collection<? extends E> c) { //创建默认TreeMap和TreeSet,再将集合元素全部添加到TreeMap中 this(); addAll(c); } public TreeSet(SortedSet<E> s) { //通过有序Set指定的排序方法、以及它的所有元素创建TreeMap、TreeSet this(s.comparator()); addAll(s); }
其他方法比较简单,基本上所有接口方法的实现底层都是调用的TreeMap对象的相关方法来实现。源码如下:
//批量添加 public boolean addAll(Collection<? extends E> c) { //限定c的为三种类型之一 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); } //迭代器 public Iterator<E> iterator() { return m.navigableKeySet().iterator(); } //降序迭代器 public Iterator<E> descendingIterator() { return m.descendingKeySet().iterator(); } //并行迭代器 public Spliterator<E> spliterator() { return TreeMap.keySpliteratorFor(m); } //返回有序(降序)Set public NavigableSet<E> descendingSet() { return new TreeSet<>(m.descendingMap()); } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } 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 NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { return new TreeSet<>(m.subMap(fromElement, fromInclusive, toElement, toInclusive)); } public NavigableSet<E> headSet(E toElement, boolean inclusive) { return new TreeSet<>(m.headMap(toElement, inclusive)); } public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { return new TreeSet<>(m.tailMap(fromElement, inclusive)); } 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(); } public E lower(E e) { return m.lowerKey(e); } public E floor(E e) { return m.floorKey(e); } public E ceiling(E e) { return m.ceilingKey(e); } public E higher(E e) { return m.higherKey(e); } public E pollFirst() { Map.Entry<E,?> e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } 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); }