JAVA源码学习之集合-HashSet

目录

前言

正文

类的描述

常量

构造方法

添加

移除

结论


前言

前面有提到,Set是不可重复的且无序的。这章我们要讲的的就是Set 的第一种实现方式, HashSet.

正文

类的描述

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable
这个类实现了 Set接口,支持一个哈希表(实际上 HashMap实例)。没有担保的迭代顺序组;特别是,它并不能保证订单将随时间保持不变。这个类允许 null元素。 
这个类提供了持续的时间性能的基本操作(add、remove contains和size),假设哈希函数分散元素正确的桶中。遍历这组需要时间成比例的总和HashSet实例的大小(元素)的数量加上HashMap支持实例的“能力”(桶的数量)。因此,它是非常重要的不是设置初始容量太高(或负荷系数太低)如果迭代性能是很重要的。

注意,此实现不是同步的。如果多个线程同时访问一组散列,和至少一个线程修改设置,它必须同步外部。这通常是通过自然对象封装了一组同步。如果没有这样的对象存在,应该集“wrapped"使用Collections.synchronizedSet方法。最好在创建时完成,以防止意外的不同步访问设置:

集合s =集合。synchronizedSet(new HashSet(…));
这类的iterator方法返回的迭代器快速失败:如果设置修改创建迭代器后,任何时候以任何方式除非通过迭代器的remove方法,迭代器抛出ConcurrentModificationException。并发修改,因此,面对迭代器快速、清晰地失败,而不是冒着任意,不确定的行为在未来一个不确定的时间。

注意,迭代器的快速失败行为不能得到保证,一般来说,不可能努力做出任何担保的存在不同步的并发修改。快速失败迭代器扔ConcurrentModificationException力所能及。因此,编写一个程序,这将是错误的依赖这个异常的正确性:迭代器的快速失败行为应该只用来检测错误。

这个类是 Java Collections Framework的一员。

从以下版本开始: 
1.2 
另请参见: 
Collection、 Set TreeSet、 HashMap Serialized Form 

常量

 private transient HashMap<E,Object> map;

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

根据常量和类描述,猜想HashSet其实是用HashMap实现的,因为HashMap Key 是不可重复的

下面所有相关的HashMap都会在研究HashMap的时候在详细解释,这里就大致提一下,有兴趣和苦恼的同学可以自己研究,和查资料

构造方法

   //默认构造函数
   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);
    }

    /**
     * Constructs a new, empty linked hash set.  (This package private
     * constructor is only used by LinkedHashSet.) The backing
     * HashMap instance is a LinkedHashMap with the specified initial
     * capacity and the specified load factor.
     *
     * @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的资料LiskHashSet来用的,我们在下面的篇章会介绍
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

上面的构造函数,其实都是根据HashMap的构造函数来实现的, 关于

添加

 //添加一个元素, 使用的hashMap的put, key为set当前元素,然后Value为常量 PRESENT
 public boolean add(E e) {
        return map.put(e, PRESENT)==null; 
    }
//为什么要 == null,是因为HashMap在put进入新对象的时候会返回null, 如果原来存在key会返回原来元素
//而Set不可重复,所以通过 == null 来判断每次插入key是不是新的,和set里面的元素是不是重复的
// hashMap代码
 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        。。。。。。
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
// hashSet中的addAll其实调用的是,父类的父类AbstractConllection的addAll方法

public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e)) //这里使用了add方法,是HashSet自己实现的
                modified = true;
        return modified;
    }
//所以addAll方法的原理就是使用调用当前循环调用当前类的add方法

移除


public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
//调用hashMap的Map 为什么要判断==PRESENT呢
//是因为 hashMap在remove的时候会返回value, 而HashSet add的时候map的value一直都是PRESENT
 public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

结论

HashSet是通过HashMapKey不可重复来实现的key不重复,基本的操作都是通过hashMap来实现的,所以想要更好的理解HashSet, 我们就要深入了解HashMap, HashMap在接下来的Map相关的篇章里面会讲到,如果同学比较着急的话,可以自己研究,查找资料

上一章                                                                                                                                 下一章

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值