HashSet和TreeSet源码分析

   Set是一个常见接口,用来保存不同的元素。常用的实现了Set接口的类有HashSet,TreeSet。一个底层基于HashMap实现,另一个基于TreeMap实现,理解了上述两个Map之后分析Set源码就比较简单了。


HashSet源码分析

    hashset 基于HashMap实现,把传入的值作为HashMap的key,由于底层HashMap的key是不可能重复的,因此HashSet也是不会重复的,即其中包含的所有元素都不重复,只保存一份。

重要参数及构造函数

    private transient HashMap<E,Object> map;//底层是一个HashMap

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    /**
     *  构造一个默认的HashSet,底层的hashMap容量是16,负载因子是0.75
     */
    public HashSet() {
        map = new HashMap<>();
    }

    /**
     * 构造一个指定集合的HashSet,底层的HashMap容量是集合大小/0.75和默认容量的最大值
     * 然后把集合的值放入HashSet中
     * @param c the collection whose elements are to be placed into this set
     * @throws NullPointerException if the specified collection is null
     */
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    /**
     *  使用所给的容量和构造因子构造一个HashMap
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    /**
     * 只包含容量,负载因子默认0.75
     *
     * @param      initialCapacity   the initial capacity of the hash table
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero
     */
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * 使用LinkedHashMap来实现底层。
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @param      dummy             ignored (distinguishes this
     *             constructor from other int, float constructor.)
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

HashSet 的重要方法。HashSet方法不多。

  /**
     * 返回底层map的大小
     *
     * @return the number of elements in this set (its cardinality)
     */
    public int size() {
        return map.size();
    }

    /**
     * 向Set中添加一个元素,这里调用map的put方法,由于放入的元素当做key值,所有的value都是固定的,因此HashMap的底层数组上
     *始终都只有一个元素,不会产生链表。相当于把key值算出hash值后放在对应的桶中。
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    /**
     * 删除一个元素,调用map的remove方法,找到对应的key值然后删除即可。
     * @param o object to be removed from this set, if present
     * @return <tt>true</tt> if the set contained the specified element
     */
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    /**
     * 调用map的remove方法清除。
     * The set will be empty after this call returns.
     */
    public void clear() {
        map.clear();
    }

    /**
     * 返回一个浅复制对象。
     *
     * @return a shallow copy of this set
     */
    @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);
        }
    }

 TreeSet源码分析

 一些参数和构造方法。

 /**
     * 底层map
     */
    private transient NavigableMap<E,Object> m;

    // 用作TreeMap中插入的value值,都是一样的保证hashcode相等
    private static final Object PRESENT = new Object();

    /**
     *   使用map构造
     */
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    /**
     * 构造一个自然排序的TreeMap,并调用上一个构造方法,传入值也就是新的TreeMap。
     */
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

    /**
     * 传入一个比较器,并且构造一个TreeMap调用上面的构造方法。
     */
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

    /**
     * 使用一个集合作为参数,然后调用addall方法把集合添加到treeset中。
     */
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    /**
     * 构造一个包含相同元素的新树集合,使用指定的排序集合进行相同的排序。
     *
     * @param s sorted set whose elements will comprise the new set
     * @throws NullPointerException if the specified sorted set is null
     */
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }


TreeSet中的重要方法。整体上都是调用Treemap 的方法,比较简单。

  

    /**
     * 将set逆序输出,调用addAll来添加一个新的逆序map。
     */
    public NavigableSet<E> descendingSet() {
        return new TreeSet<>(m.descendingMap());
    }

    /**
     * 返回size
     *
     * @return the number of elements in this set (its cardinality)
     */
    public int size() {
        return m.size();
    }

    /**
     *  判断是否为空
     * @return {@code true} if this set contains no elements
     */
    public boolean isEmpty() {
        return m.isEmpty();
    }

    /**
     * Returns {@code true} if this set contains the specified element.
     * More formally, returns {@code true} if and only if this set
     * contains an element {@code e} such that
     * <tt>(o==null ? e==null : o.equals(e))</tt>.
     *
     * @param o object to be checked for containment in this set
     * @return {@code true} if this set contains the specified element
     * @throws ClassCastException if the specified object cannot be compared
     *         with the elements currently in the set
     * @throws NullPointerException if the specified element is null
     *         and this set uses natural ordering, or its comparator
     *         does not permit null elements
     */
    public boolean contains(Object o) {
        return m.containsKey(o);
    }

    /**
     * 将元素作为key通过TreeMap方法添加到树中。
     */
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

    /**
     * 调用TreeMap底层remove方法删除元素,若存在则和PRESEET对比,肯定返回true,否则的话若元素不存在则返回false。
     */
    public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }

    /**
     * 删除所有元素。
     */
    public void clear() {
        m.clear();
    }

    /**
     *将指定集合中的所有元素添加到这个set集合中去。
     *
     */
    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) {//传入的参数是一个排序map,底层要是一个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);
    }

 TreeSet中的一些查看和删除方法。

 /**
     * 查找排序树上第一个的元素(一般类似于最左子树这样)
     */
    public E first() {
        return m.firstKey();
    }

    /**
     * 查找排序树上最后一个元素
     */
    public E last() {
        return m.lastKey();
    }

    // NavigableSet API methods

    

    /**
     *小于e的最大的key。
     */
    public E floor(E e) {
        return m.floorKey(e);
    }

    /**
     *大于e的最小的key
     * @since 1.6
     */
    public E ceiling(E e) {
        return m.ceilingKey(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();
    }

总结

    整体而言set实在是比较简单的,都是调用map里的方法,重要的思想也都在map中有所体现,这里就不过多赘述了。






猜你喜欢

转载自blog.csdn.net/pb_yan/article/details/80368244