2023-01-14  阅读(3)
原文作者: HelloWorld_EE 原文地址:https://blog.csdn.net/u010412719/category_6159934_2.html

《Java源码分析》:HashSet

前面几篇把Map、List相关的的几个类看了下,当然Set接口相关的类也相当的重要,我们也用的比较多,因此,我们也需要了解下Set接口的两个实现类HashSet、TreeSet的源码。这篇博文是记录HashSet的源码分析。下篇博文就来记录TreeSet的源码分析。

如果想了解其他的源码分析,可点击下面相对应的连接哈。

HashMap类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51980632

Hashtable类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51972602

LinkedHashMap类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51984455

Vector类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51992819

Stack类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51995168

ArrayList类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51108357

LinkedList类的源码分析在这里:http://blog.csdn.net/u010412719/article/details/51124350

说明:我们都知道HashSet集合是不允许重复元素的,因此这个类的利用什么来保证这个集合里面不会有重复的元素呢?结论是:HashSet是借助于HashMap的key不允许重复这个特性来实现的。想想这个思想确实比较奇妙,是吧,HashMap是操作键值对,而HashSet是操作HashMap的key完成相关操作,或者这么说,HashSet全部的操作是借助于HashMap经过某种封装得到的

下面来开始具体的分析,其实,如果我们了解HashMap的内部实现,HashSet的内部实现就相当的容易理解。

1、HashSet的继承结构

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

HashSet继承了AbstractSet抽象类,也实现了Set、Cloneable和Serializable接口。

或许你可能有这样的疑惑:AbstractSet实现了Set接口,HashSet既要继承AbstractSet还需要实现Set接口呢??

如果你看过HashMap的源码,HashMap的继承结构和HashSet类似,,为了方便比较,还是贴一下:

    
        public class HashMap<K,V> extends AbstractMap<K,V>
            implements Map<K,V>, Cloneable, Serializable

在HashMap我们已经探讨了为什么AbstractMap实现了Map接口,HashMap又继承AbstractMap还要实现Map接口呢??

这个问题在HashMap源码分析中已经有了答案,因此,HashSet的继承结构为什么如此,我想原因应该是一样的,HashSet之所以还实现Set接口,在功能上没有任何作用,仅仅只是使结构体系更加清晰完整。

2、HashSet的属性和构造函数

从下面可以看出,HashSet有一个HashMap的引用,且有一个常量。

HashSet的构造函数,都是创建一个map实例对象。HashSet对象的默认大小与HashMap默认大小一样,是16,且大小只能是2的幂次方。

        private transient HashMap<E,Object> map;
    
        // Dummy value to associate with an Object in the backing Map
        private static final Object PRESENT = new Object();
    
        /**
         * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
         * default initial capacity (16) and load factor (0.75).
         */
        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.
         *
         */
        HashSet(int initialCapacity, float loadFactor, boolean dummy) {
            map = new LinkedHashMap<>(initialCapacity, loadFactor);
        }

3、HashSet中常见的一些方法

3.1、 add(E e)

源码如下:

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

看到这个源码是不是很爽,太简单了哈,就是将要添加的元素作为map的key,常量PRESENT作为value保存在HashMap对象中即可。有一点需要我们注意的是:这个add方法有返回值,返回值有map的put方法决定,当map调用put(key,value)方法时,如果存在key后,则map的put方法会返回此key对应的oldValue。此时add就会返回false

3.2、 iterator()

HashSet并没有提供类似于get的方法,只提供的获取访问元素的迭代器对象。iterator方法也是委托给了map,获取map的key集合的迭代器。

        public Iterator<E> iterator() {
            return map.keySet().iterator();
        }

3.3、 其它的方法

其它方法的源码如下,全部都是委托给HashMap来实现的,是不是很简单哈,这里就不再过多的进行分析。

    
    
    
        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的内部实现我们只需要记住一点:是基于HashMap来实现的,即所有方法都是委托给HashMap对象来实现的;在使用方面,也只需要记住一点:所有Set集合对象存储的元素是不允许重复的哈


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文