Java集合框架整理(七)——TreeSet、HashSet、LinkedHashSet源码分析


title: Java集合框架整理(七)——TreeSet、HashSet、LinkedHashSet源码分析
tag: Java集合
category: Java


TreeSet、HashSet、LinkedHashSet源码分析

TreeSet是基于TreeMap实现的有序的集合,HashSet是基于HashMap实现的,而LinkedHashSet更是继承自HashSet,所以他们的源码都不会太复杂,所以在看这篇文章之前需要先看TreeMap源码分析HashMap的源码分析

TreeSet

有序的Set集合

基于TreeMap实现

TreeSet的继承关系

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    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);
    }
    ...
}

TreeSet继承自AbstractSet,这是之前Java集合框架整理看过了。然后还实现了NavigableSet、Cloneable、Serializable接口,重点看看NavigableSet(另外两个就没啥说的了)

在看NavigableSet接口之前,先看看TreeSet中的全局变量和构造器吧。
m是一个NavigableMap,也是一个接口,具体实现是TreeMap(详见TreeMap源码分析),TreeSet很多方法最后都是调用TreeMap来实现的,TreeSet就是一个壳子。PRESENT就是一个假想值。通过观看几个重载的构造方法,最终是以一个TreeMap来初始化的,所以才说TreeSet是基于TreeMap实现的;Set是继承Collection的,所以也可以通过它来初始化,Collection衍生的类(如ArrayList、LinkedList等都有类似的构造方法);TreeSet还可以通过SortSet来构造,SortSet也是一个接口,它是下一步要看的NavigableSet的父接口

接着看NavigableSet接口吧

public interface NavigableSet<E> extends SortedSet<E> {
    //返回Set中小于e的最大元素
    E lower(E e);
    //返回Set中小于等于e的最大元素
    E floor(E e);
    //返回Set中大于等于e的最小元素
    E ceiling(E e);
    //返回Set中大于e的最小元素
    E higher(E e);
    //检索并移除Set中最小的元素(第一个)
    E pollFirst();
    //检索并移除Set中最大的元素(最后一个)
    E pollLast();
    //迭代器
    Iterator<E> iterator();
    //逆序的Set结合
    NavigableSet<E> descendingSet();
    //逆序的迭代器
    Iterator<E> descendingIterator();
    //批量处理,并发执行
    NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                           E toElement,   boolean toInclusive);
    //返回小于toElement的Set结合
    NavigableSet<E> headSet(E toElement, boolean inclusive);
    //返回大于fromElement的Set集合
    NavigableSet<E> tailSet(E fromElement, boolean inclusive);
    SortedSet<E> subSet(E fromElement, E toElement);
    SortedSet<E> headSet(E toElement);
    SortedSet<E> tailSet(E fromElement);
}

NavigableSet又是继承自SortedSet,NavigableSet中定义了一些操作方法,具体实现则在TreeSet中

接着看看SortedSet

public interface SortedSet<E> extends Set<E> {
    //排序方式
    Comparator<? super E> comparator();

    SortedSet<E> subSet(E fromElement, E toElement);

    SortedSet<E> headSet(E toElement);

    SortedSet<E> tailSet(E fromElement);
    //获取排序后的Set中的第一个
    E first();
    //获取排序后的Set中的最后一个
    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();
            }
        };
    }
}

SortedSet名如其意,就是排序的Set,是直接继承的Set接口,前面我们能知道Set基本跟Collection差不多,就是多了默认的分割迭代器。在SortedSet中添加了几个方法,便于一些常用的操作,如first()、last()分别得到当前Set中的第一个和最后一个元素;同时SortedSet中重写了spliterator(),修改了默认分割迭代器,得到的是可比较的分割迭代器,这个迭代器又是根据comparator()生成的,所以comparator()就需要看具体实现了;subSet()和subList()是一样的,集合操作,返回两个集合的交;headSet()返回小于toElement集合的所有元素组成的集合;同理,tailSet()返回大于等于fromElement;comparator()是定义排序方式的,这个接口中也没多少方法,提供了一些排序后获取元素的方法

所以不管是NavigableSet还是SortedSet,代码不是很多,都是定义了一些有序的操作方法,接着继续看TreeSet中的具体实现吧

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    ...
    //迭代器,返回的就是TreeMap的
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }
    //逆序迭代器
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }

    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  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);
    }

    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();
    }

    // NavigableSet API methods
    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;
    }

    //返回TreeMap的分割迭代器
    public Spliterator<E> spliterator() {
        return TreeMap.keySpliteratorFor(m);
    }
}

看着代码很多,其实仔细看看,很多方法都是基于TreeMap的,根本就没啥实际性的逻辑,所以一定要看完TreeMap的源码

HashSet

HashSet是基于HashMap实现的,所以也不用多说什么,直接看看源码吧

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
    private transient HashMap<E,Object> 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);
    }

    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    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);
        }
    }
    ...
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }
}

通过源码可以知道,大多数api的调用都是交给了内部的HashMap去处理了,所以原理需要看看HashMap源码分析

LinkedHashSet

LinkedHashSet是继承自HashSet的,而它本身也没有很多方法,所以依然是内部使用HashMap

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

    private static final long serialVersionUID = -2851667679971038690L;
    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,不同的就是实例化的时候,参数不同得到了LinkedHashSet,准确来说,LinkedHashSet就是HashSet的一种特殊情况

默认容量是16,加载因子是0.75,依然要满足HashMap的2次幂的要求

总结

TreeSet、HashSet分别基于TreeMap、HashMap实现,而LinkedHashMap是继承自HashSet,算是HashSet的一种特殊情况。看完源码,其实他们之间没有什么大的区别,最大的区别就是基于不同的map来实现的,他们本事实现了不同的接口也是因为特性提供了不同的api,更通俗一点,他们就是一个空壳,所以要知道原理还得看TreeMap源码分析HashMap源码分析

发布了43 篇原创文章 · 获赞 10 · 访问量 6998

猜你喜欢

转载自blog.csdn.net/baidu_36959886/article/details/90900392