jdk源码——集合(HashSet)

       看过HashMap的源码之后,在看HashSet集合简直是so Easy,仅仅从代码的数量上就可以看出来了。

       这一篇的分析顺序是Set,AbstractSet,HashSet。

       Set感觉没有什么可分析的。

public interface Set<E> extends Collection<E> {//Set接口继承了Collection接口

    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);
    }
}
        这是AbstractSet类的源代码,是抽象的。
/*
继承AbstractCollection,实现Set
 */
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {

    protected AbstractSet() {
        
    }
    
    /*
    比较,都是这一种,容器的比较方式都是一样的,1.判断是否是本对象 2.判断类型  3.判断长度  4.遍历,一个一个比较
     */
    public boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }
    
   /*
    前面也说过了,容器的hash值为每个元素的hash值相加
   */
    public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }
    
    /*
    删除指定的集合中所包含的所有元素
     */
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);//判断该对象是否为空
        boolean modified = false;

        if (size() > c.size()) {
            //判断两个集合的长度,遍历的是长度短的集合,很厉害,以前从来没有想到过,这样可以提高效率
            for (Iterator<?> i = c.iterator(); i.hasNext(); )
                modified |= remove(i.next());//直接删除,因为remove()方法中会判断,包含该节点直接删除,不包含,则不会
        } else {
            for (Iterator<?> i = iterator(); i.hasNext(); ) {
                if (c.contains(i.next())) {
                    i.remove();
                    modified = true;
                }
            }
        }
        return modified;
    }
}

    

HashSet底层是一个Map集合,HashSet的存储只用到了key值部分。

         先看一下HashSet集合的定义和成员变量。

/*
HashSet底层就是一个HashMap,HashSet只是用了HashMap的key,value值并未使用,存入的都是一个Object类型对象的引用
 */
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;//HshSet底层是一个HashMap,
    
    private static final Object PRESENT = new Object();//present

     再看一下HashSet的构造方法(有四个)。HasHMap集合的构造方法也是四个。这是凑效吗?其实不是的,因为其底层是HashMap集合,也就是其实就是在HashSet集合的构造方法中创建一个HashMap对象。HashMap的创建方式有四种,可以看一下源码。就是根据HashMap衍生出来的。

    /*
    HashSet(Collection<? extends E> c),HashSet(int initialCapacity, float loadFactor),HashSet(int initialCapacity)
    这三个构造方法,其实就是根据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);
    }
    
    /*
    这个构造方法,修饰符是默认的,只能在该包下访问,底层是一个LinkedHashMap
    就是这个构造方法,其子类的LinkedHashset的构造方法都是调用的这个构造方法
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

      其实还有一个构造方法,他的修饰符是默认的,创建LinkedHashSet集合时,一定会调用这个构造方法。

    /*
    这个构造方法,修饰符是默认的,只能在该包下访问,底层是一个LinkedHashMap
    就是这个构造方法,其子类的LinkedHashset的构造方法都是调用的这个构造方法
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    HashSet的一众方法,均是调用HashMap集合的方法。

    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();
    }
HashSet集合还有两个私有方法,一个readObject()和writeObject(),都是为了实现序列化和反序列化
 
 



      

      

猜你喜欢

转载自blog.csdn.net/qq_36838854/article/details/80358369