Java集合源码分析11----HashSet源码分析

目录

 

简介

介绍

方法源码

迭代方式


-----------源码分析基于jdk1.8.

简介

    HashSet自jdk1.2引入,继承了抽象方法AbstractSet,实现了Set接口,Cloneable接口,以及java.io.Serializable接口,所以能被克隆和序列化。

    HashSet底层是使用HashMap实现的,其中HashMap的键就是HashSet集合的元素,所以HashSet集合是一个元素不重复的集合,并且不保证元素的顺序,容许存储null值

    此外HashSet不是同步的,在多线程环境中使用时,需要外部进行同步处理,最好在创建时使用Set s = Collections.synchronizedSet(new HashSet(...)),以防止对于Set偶发非同步访问。

介绍

1.构造方法

  //构建一个HashSet,底层使用的HashMap实例是默认的初始大小16,负载因子为0.75
    public HashSet() {
        map = new HashMap<>();
    }

    //构建一个包含指定集合元素的HashSet,底层使用的HashMap是默认负载因子(0.75)以及能容纳指定集合的元素的初始容量
    //如果指定集合c为null,将抛出NullPointerException异常.
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

    //构建一个HashSet,底层使用的HashMap实例具有指定的初始容量和指定负载因子。
    //初始容量小于0或者负载因子为非正数,将抛出IllegalArgumentException异常。
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    //构建一个HashSet,底层使用的HashMap实例具有指定初始容量和默认的负载因子(0.75)
    //如果指定的初始容量小于0,将抛出IllegalArgumentException异常。
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    //构建一个HashSet,此构造器只限于LinkedHashSet使用,底层使用是LinkedHashMap,具有指定初始容量和指定的负载因子。
    //初始容量小于0或者负载因子为非正数,将抛出IllegalArgumentException异常。
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    HashSet的底层使用HashMap实现的,所以受到初始容量initialCapacity和负载因子LoadFactor的影响,构造有参构造方法时传入这两个变量的值,以调整底层HashMap的性能。默认的负载因子是0.75,是时间成本和空间成本上的一种权衡。HashMap中如果没有指定capacity,默认容量是16,在指定两个参数的情况下可以通过threshold = capacity * loadFactor来计算capacity,第二个构造方法中Math.max((int) (c.size()/.75f) + 1, 16),表示计算后容量没有超过16情况下,HashMap的初始容量设置为16。

2.内部变量

    //HashSet底层就是通过HashMap来保存数据,HashSet的元素是HashMap中的值。
    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    //HashMap保存的数据是键-值对映射,HashSet中的值是HashMap的键
    //而HashMap的值用固定的对象PRESENT来代替
    private static final Object PRESENT = new Object();

    HashMap存储的是键-值对映射,HashSet集合中的元素就是HashMap的键,而HashMap的值就是用固定的Object对象PRESENT来代替。

方法源码

    Hashset方法实现依靠HashMap,可以看到HashSet中大部分的方法都会直接调用HashMap中的方法。

    //返回HashSet的迭代器,元素没有顺序。
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    //返回此HashSet中元素的数量.
    public int size() {
        return map.size();
    }

    //如果此HashSet不包含任何元素,返回true.
    public boolean isEmpty() {
        return map.isEmpty();
    }

   //如果此HashSet包含指定的元素o,将返回true
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    //向HashSet中添加指定的元素e,添加成功返回true。
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

    //从HashSet中移除指定的元素o.
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    //清空HashSet中所有的元素
    public void clear() {
        map.clear();
    }

    //返回此HashSet的浅度克隆
    @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);
        }
    }

    //将此HashSet实例状态保存到流中(序列化)
    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
        //HashMap的容量和负载因子
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        //HashMap的实际大小
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        //所有的元素
        for (E e : map.keySet())
            s.writeObject(e);
    }

    //从流中重新构建一个HashSet(反序列化)
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        //读取容量
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        //读取负载因子
        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);
        }
        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum 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));

        // Create backing HashMap
        //创建底层使用的是HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        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);
    }

迭代方式

    1.通过Iterator迭代器遍历HashSet,调用方法iterator()返回迭代器。

    2.使用foreach循环。

public class IteratorHashSet {
  public static void main(String[] args) {
    Set<String> sets = new HashSet<>();
    for(String word:"one,two,three,four".split(",")) {
      sets.add(word);
    }
    iteratorHashSet1(sets);
    iteratorHashSet2(sets);
  }

  private static void iteratorHashSet1(Set<String> set) {
     Iterator<String> iterator = set.iterator();
     while(iterator.hasNext()) {
       System.out.print(iterator.next()+" ");
     }
     System.out.println("--------");
  }

  private static void iteratorHashSet2(Set<String> set) {
    for(String str:set) {
      System.out.print(str+" ");
    }
    System.out.println("----------");
    //jdk1.8
    set.forEach(s->System.out.print(s+" "));
    System.out.println("----------");
    set.forEach(System.out::print);
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值