我的jdk源码(十五):HashSet类

一、概述

    HashSet类是常用的Set集合类,底层是采用HashMap来实现的,我们通常利用HashSet类的元素不重复的特性来达到去重的效果,那么跟随源码去一探究竟吧。

二、源码分析

    (1) 类的声明

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

    在类的声明中我们可以看到,HashSet类是继承自AbstractSet类,HashSet类可以复用父类的一些方法,同时HashSet类还是实现了Set接口,以及Cloneable接口和Serializable。

    * AbstractSet继承自AbstractCollection,实现了Set接口,此类并没有重写 AbstractCollection 类中的任何实现(包括add()方法)。它仅仅添加了 equals 和 hashCode 的实现。

    *  实现了Set接口表示本类是一个不包含重复元素的类,并最多包含一个null值。

    *  实现Cloneable接口表示可以调用Object.clone方法返回该对象的浅拷贝。

    *  实现Serializable接口表示可以启用其序列化功能,能通过序列化去传输。

    (2) 成员变量

    //序列化标记ID
    static final long serialVersionUID = -5024744406713321676L;
    //实际存放元素的map,类型是HashMap,所以HashSet是依赖HashMap的
    private transient HashMap<E,Object> map;
    //final修饰的不可改变的空对象
    private static final Object PRESENT = new Object();

    底层通过HashMap来进行数据的存储,通过下面的源码可以得知采用HashMap的key来存储元素,value统一存储一个不可改变的空对象PRESENT,这样既保证HashMap的存储结构,数据的移除操作也是通过HashMap的remove方法进行移除,而HashMap的remove方法会返回被移除的Value值,而把PRESENT设为final就能保证可以通过判断返回的值是否是PRESENT对象就可以判断是否移除成功。

    (3) 构造方法

    //无参构造函数
    public HashSet() {
        //初始化map为一个空的HashMap
        map = new HashMap<>();
    }

    //带容量的构造函数
    public HashSet(int initialCapacity) {
        //初始化一个容量参数为initialCapacity的HashMap,但是最后map的容量不一定是initialCapacity,而是大于等于initialCapacity的最小2次幂
        map = new HashMap<>(initialCapacity);
    }

    //带容量和负载因子的构造函数
    public HashSet(int initialCapacity, float loadFactor) {
        //在上一个方法基础上,多了设置负载因子这一步
        map = new HashMap<>(initialCapacity, loadFactor);
    }

    //带容量和负载因子以及标记dummy的构造函数,但是没有public修饰。dummy标记是用来区分HashSet(int initialCapacity, float loadFactor)方法
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    
    //传入一个集合c的构造函数
    public HashSet(Collection<? extends E> c) {
        //创建一个足够大的map,容量计算规则是取(c.size()/.75f)+1和16取较大的值作为initialCapacity,但是这也不是最终的容量,最终的容量还是大于initialCapacity的最小2次幂
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        //添加集合c中的所有元素到map中
        addAll(c);
    }

    (4) add()方法

    //添加一个元素
    public boolean add(E e) {
        //调用HashMap的put方法,因为HashMap的Key不能重复,重复时会把添加的元素直接返回,成功则返回null
        return map.put(e, PRESENT)==null;
    }

    HashSet类为什么能去重呢?它的add()方法告诉了我们答案,HashSet在调用set()方法的时候,是把添加的元素作为了HashMap的key,而value就是上面final修饰的空对象PRESENT。我看到这儿我惊了, HashSet利用HashMap的key不能重复的机制,因为key重复就覆盖value,以此来达到去重的效果,我只能说绝了!

    (5) 其余核心方法

    //调用HashMap的clear方法来清空元素
    public void clear() {
        map.clear();
    }

    //调用HashMap的containsKey来判断是否包含元素o
    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    //通过调用HashMap的isEmpty方法来判断集合是否为空
     public boolean isEmpty() {
        return map.isEmpty();
    }

    //返回迭代器
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }

    //调用HashMap的remove方法来移除元素
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }

    //HashMap的大小就是集合的大小
    public int size() {
        return map.size();
    }

    HashSet类的其他方法也比较简单,都是直接调用HashMap的方法,如果你对HashMap还不熟,那我推荐你看一下《我的jdk源码(十三):HashMap 一磕到底,追根溯源!》。

三、总结

    HashSet类还是有几点比较重要的知识点,罗列如下:

    * HashSet底层依赖HashMap,所以也是线程不安全的。

    * HashSet底层依赖HashMap,所以支持动态扩容,但也因此和HashMap一样,无法保证元素的有序性。

    * 我们可以利用HashSet的元素不重复特性来达到为集合去重的效果。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

利用 TensorFlow 训练自己的目标识别器。本文内容来自于我的毕业设计,基于 TensorFlow 1.15.0,其他 TensorFlow 版本运行可能存在问题。.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java觉浅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值