zoukankan      html  css  js  c++  java
  • java源码阅读HashSet

    1类签名与注解

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

    此类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代次序不作任何保证。特别是,它不能保证顺序在一段时间内保持不变(HashMap的扩容重hash)。 这个类允许null元素。

    请注意,此实现不同步。 如果多个线程并发访问哈希集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,那么该集合应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:

     Set s = Collections.synchronizedSet(new HashSet(...)); 

    该类iterator方法返回的迭代器是故障快速的。此机制在HashMap一节中有详细讲述。

    2属性

    static final long serialVersionUID = -5024744406713321676L;
    
    private transient HashMap<E,Object> map;
    
    // Map中的一个虚拟值
    private static final Object PRESENT = new Object();

    HashSet是通过HashMap实现的,所以内部持有map的引用,Set的值对应着Map中的key,但是每次map的插入需要是<key,value>的键值对,所以就有了虚拟的value对象,就是PRESENT。

    3构造方法

    //1默认
    public HashSet() {
            map = new HashMap<>();
        }
    
    //2通过集合构造
    public HashSet(Collection<? extends E> c) {
            map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
            addAll(c);
        }
    
    //3指定HashMap的初始化容量和负载因子
    public HashSet(int initialCapacity, float loadFactor) {
            map = new HashMap<>(initialCapacity, loadFactor);
        }
    
    //4指定HashMap的初始化容量
    public HashSet(int initialCapacity) {
            map = new HashMap<>(initialCapacity);
        }
    
    //5指定初始化容量和负载因子,内部通过LinkedHashMap实现
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
            map = new LinkedHashMap<>(initialCapacity, loadFactor);
        }

    注意:构造方法5的dummy参数其实没有特别的意义,唯一作用是通过多一个参数来区别构造方法3(方法重载)。

    4常用方法

    (1)add

    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }

    (2)remove

    public boolean remove(Object o) {
            return map.remove(o)==PRESENT;
        }

    (3)contains

    public boolean contains(Object o) {
            return map.containsKey(o);
        }

    (4)其他常用

    public Iterator<E> iterator() {
            return map.keySet().iterator();
        }
    
    public int size() {
            return map.size();
        }
    
    public boolean isEmpty() {
            return map.isEmpty();
        }

    HashSet的常用方法都是通过调用HashMap的方法实现的。

    5其他

    (1)clone

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

    (2)序列化与反序列化

    //序列化
    private void writeObject(java.io.ObjectOutputStream s)
            throws java.io.IOException {
            // Write out any hidden serialization magic
            s.defaultWriteObject();
    
            // Write out HashMap capacity and load factor
            s.writeInt(map.capacity());
            s.writeFloat(map.loadFactor());
    
            // Write out size
            s.writeInt(map.size());
    
            // Write out all elements in the proper order.
            for (E e : map.keySet())
                s.writeObject(e);
        }
    
    //反序列化  
    private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            // Read in any hidden serialization magic
            s.defaultReadObject();
    
            // 读capacity(检查非负).
            int capacity = s.readInt();
            if (capacity < 0) {
                throw new InvalidObjectException("Illegal capacity: " + capacity);
            }
    
            // 读负载因子(不能为null).
            float loadFactor = s.readFloat();
            if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
                throw new InvalidObjectException("Illegal load factor: " + loadFactor);
            }
    
            // Read size and verify non-negative.
            int size = s.readInt();
            if (size < 0) {
                throw new InvalidObjectException("Illegal size: " + size);
            }
            // 计算需要的capacity
            capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                    HashMap.MAXIMUM_CAPACITY);
    
            // Constructing the backing map will lazily create an array when the first element is
            // added, so check it before construction. Call HashMap.tableSizeFor to compute the
            // actual allocation size. Check Map.Entry[].class since it's the nearest public type to
            // what is actually created.
    
            SharedSecrets.getJavaOISAccess()
                         .checkArray(s, Map.Entry[].class, HashMap.tableSizeFor(capacity));
    
            // 构造HashMap对象
            map = (((HashSet<?>)this) instanceof LinkedHashSet ?
                   new LinkedHashMap<E,Object>(capacity, loadFactor) :
                   new HashMap<E,Object>(capacity, loadFactor));
    
            // 往HashMap中添加键值对
            for (int i=0; i<size; i++) {
                @SuppressWarnings("unchecked")
                    E e = (E) s.readObject();
                map.put(e, PRESENT);
            }
        }

    (3)equals

    HashSet类中没有实现equls方法,而是从父类AbstractSet中继承过来的。AbstractSet中equls实现如下:

    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;
            }
        }
    
    public boolean containsAll(Collection<?> c) {
            for (Object e : c)
                if (!contains(e))
                    return false;
            return true;
        }
    
    public boolean contains(Object o) {
            Iterator<E> it = iterator();
            if (o==null) {
                while (it.hasNext())
                    if (it.next()==null)
                        return true;
            } else {
                while (it.hasNext())
                    if (o.equals(it.next()))
                        return true;
            }
            return false;
        }

    equals首先判断是否引用同1个对象,若是则返回true。

    否则,判断是否都是Set类型的,若不是则返回false。

    若是,判断是否都有相等的size,若不是则返回false。

    若是,则调用containsAll是否包含所有元素,若是则返回true,否则返回false。

  • 相关阅读:
    经典网络还是VPC,开发者作何选择?
    经典网络还是VPC,开发者作何选择?
    文件系统的几种类型:ext3, swap, RAID, LVM
    文件系统的几种类型:ext3, swap, RAID, LVM
    ★商场上的十则寓言故事!
    ★商场上的十则寓言故事!
    【★】自制网络心理需求大排名!
    【★】自制网络心理需求大排名!
    自制mpls ldp实验
    自制mpls ldp实验
  • 原文地址:https://www.cnblogs.com/ouym/p/9007389.html
Copyright © 2011-2022 走看看