一、  HashSet概述:

HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。

二、  HashSet的实现:

对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,

HashSet的源代码如下:

1 public class HashSet<E>
2    extends AbstractSet<E>
3    implements Set<E>, Cloneable, java.io.Serializable
4{
5    static final long serialVersionUID = -5024744406713321676L;
6
7    // 底层使用HashMap来保存HashSet中所有元素。
8    private transient HashMap<E,Object> map;
9
10    // 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。
11    private static final Object PRESENT = new Object();
12
13    /**
14     * 默认的无参构造器,构造一个空的HashSet。
15     *
16     * 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。
17     */
18    public HashSet() {
19    map = new HashMap<E,Object>();
20    }
21
22    /**
23     * 构造一个包含指定collection中的元素的新set。
24     *
25     * 实际底层使用默认的加载因子0.75和足以包含指定
26     * collection中所有元素的初始容量来创建一个HashMap。
27     * @param c 其中的元素将存放在此set中的collection。
28     */
29    public HashSet(Collection<? extends E> c) {
30    map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
31    addAll(c);
32    }
33
34    /**
35     * 以指定的initialCapacity和loadFactor构造一个空的HashSet。
36     *
37     * 实际底层以相应的参数构造一个空的HashMap。
38     * @param initialCapacity 初始容量。
39     * @param loadFactor 加载因子。
40     */
41    public HashSet(int initialCapacity, float loadFactor) {
42    map = new HashMap<E,Object>(initialCapacity, loadFactor);
43    }
44
45    /**
46     * 以指定的initialCapacity构造一个空的HashSet。
47     *
48     * 实际底层以相应的参数及加载因子loadFactor为0.75构造一个空的HashMap。
49     * @param initialCapacity 初始容量。
50     */
51    public HashSet(int initialCapacity) {
52    map = new HashMap<E,Object>(initialCapacity);
53    }
54
55    /**
56     * 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。
57     * 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。
58     *
59     * 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。
60     * @param initialCapacity 初始容量。
61     * @param loadFactor 加载因子。
62     * @param dummy 标记。
63     */
64    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
65    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
66    }
67
68    /**
69     * 返回对此set中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
70     *
71     * 底层实际调用底层HashMap的keySet来返回所有的key。
72     * 可见HashSet中的元素,只是存放在了底层HashMap的key上,
73     * value使用一个static final的Object对象标识。
74     * @return 对此set中元素进行迭代的Iterator。
75     */
76    public Iterator<E> iterator() {
77    return map.keySet().iterator();
78    }
79
80    /**
81     * 返回此set中的元素的数量(set的容量)。
82     *
83     * 底层实际调用HashMap的size()方法返回Entry的数量,就得到该Set中元素的个数。
84     * @return 此set中的元素的数量(set的容量)。
85     */
86    public int size() {
87    return map.size();
88    }
89
90    /**
91     * 如果此set不包含任何元素,则返回true。
92     *
93     * 底层实际调用HashMap的isEmpty()判断该HashSet是否为空。
94     * @return 如果此set不包含任何元素,则返回true。
95     */
96    public boolean isEmpty() {
97    return map.isEmpty();
98    }
99
100    /**
101     * 如果此set包含指定元素,则返回true。
102     * 更确切地讲,当且仅当此set包含一个满足(o==null ? e==null : o.equals(e))
103     * 的e元素时,返回true。
104     *
105     * 底层实际调用HashMap的containsKey判断是否包含指定key。
106     * @param o 在此set中的存在已得到测试的元素。
107     * @return 如果此set包含指定元素,则返回true。
108     */
109    public boolean contains(Object o) {
110    return map.containsKey(o);
111    }
112
113    /**
114     * 如果此set中尚未包含指定元素,则添加指定元素。
115     * 更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2))
116     * 的元素e2,则向此set 添加指定的元素e。
117     * 如果此set已包含该元素,则该调用不更改set并返回false。
118     *
119     * 底层实际将将该元素作为key放入HashMap。
120     * 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key
121     * 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true),
122     * 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变,
123     * 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中,
124     * 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。
125     * @param e 将添加到此set中的元素。
126     * @return 如果此set尚未包含指定元素,则返回true。
127     */
128    public boolean add(E e) {
129    return map.put(e, PRESENT)==null;
130    }
131
132    /**
133     * 如果指定元素存在于此set中,则将其移除。
134     * 更确切地讲,如果此set包含一个满足(o==null ? e==null : o.equals(e))的元素e,
135     * 则将其移除。如果此set已包含该元素,则返回true
136     * (或者:如果此set因调用而发生更改,则返回true)。(一旦调用返回,则此set不再包含该元素)。
137     *
138     * 底层实际调用HashMap的remove方法删除指定Entry。
139     * @param o 如果存在于此set中则需要将其移除的对象。
140     * @return 如果set包含指定元素,则返回true。
141     */
142    public boolean remove(Object o) {
143    return map.remove(o)==PRESENT;
144    }
145
146    /**
147     * 从此set中移除所有元素。此调用返回后,该set将为空。
148     *
149     * 底层实际调用HashMap的clear方法清空Entry中所有元素。
150     */
151    public void clear() {
152    map.clear();
153    }
154
155    /**
156     * 返回此HashSet实例的浅表副本:并没有复制这些元素本身。
157     *
158     * 底层实际调用HashMap的clone()方法,获取HashMap的浅表副本,并设置到  HashSet中。
159     */
160    public Object clone() {
161        try {
162            HashSet<E> newSet = (HashSet<E>) super.clone();
163            newSet.map = (HashMap<E, Object>) map.clone();
164            return newSet;
165        } catch (CloneNotSupportedException e) {
166            throw new InternalError();
167        }
168    }
} 

三、 相关说明:

对于HashSet中保存的对象,请注意正确重写其equals和hashCode方法,以保证放入的对象的唯一性。

转载于:https://www.cnblogs.com/ITtangtang/p/3948538.html

Java集合---HashSet的源码分析相关推荐

  1. 死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  2. 【死磕 Java 集合】— LinkedTransferQueue源码分析

    [死磕 Java 集合]- LinkedTransferQueue源码分析 问题 (1)LinkedTransferQueue是什么东东? (2)LinkedTransferQueue是怎么实现阻塞队 ...

  3. 死磕Java集合之BitSet源码分析(JDK18)

    死磕Java集合之BitSet源码分析(JDK18) 文章目录 死磕Java集合之BitSet源码分析(JDK18) 简介 继承体系 存储结构 源码解析 属性 构造方法 set(int bitInde ...

  4. java arraydeque_死磕 java集合之ArrayDeque源码分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...

  5. Java集合:Hashtable源码分析

    1. 概述 上次讨论了HashMap的结构,原理和实现,本文来对Map家族的另外一个常用集合HashTable进行介绍.HashTable和HashMap两种集合非常相似,经常被各种面试官问到两者的区 ...

  6. 死磕 java集合之ConcurrentSkipListMap源码分析——发现个bug

    前情提要 点击链接查看"跳表"详细介绍. 拜托,面试别再问我跳表了! 简介 跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表. 跳表在原有的有序链表上面增加了多级 ...

  7. Java集合之Vector源码分析

    概述 Vector与ArrayLIst类似, 内部同样维护一个数组, Vector是线程安全的. 方法与ArrayList大体一致, 只是加上 synchronized 关键字, 保证线程安全, 下面 ...

  8. Java集合干货——ArrayList源码分析

    前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体实现,一些基本的都也知道,譬 ...

  9. copyof java_死磕 java集合之CopyOnWriteArrayList源码分析

    简介 CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不 ...

最新文章

  1. Matlab怎么计算信号的能量,用Matlab求离散讯号的能量与功率怎么编程
  2. python中的cli模块_Python 快速实现CLI 应用程序的脚手架
  3. linux 硬软链接区别
  4. Angular NgTemplateOutlet的一个例子
  5. C#细说多线程(下)
  6. 刘海又回来了?iPhone 12 Pro摄像头曝光...
  7. 求一批整数中出现最多的个位数字
  8. 如何在PDF文件中插入文本或图片?
  9. cf虚拟服务器设置方法,CF和GA免费顶级域名申请注册及修改DNS域名解析方法
  10. 用javascript制作简易的QQ登录网页
  11. Using Oracle Database 11g Release 2 Result Cache in an Oracle RAC Environment
  12. 基于Halcon的高精度圆拟合算法思路
  13. Python基础之列表(list)操作
  14. 乐高机器人走进图书馆活动方案_乐高机器人制作~~乐高大派对
  15. 如何设计账户余额的数据准确性?
  16. Swift 使用NSRange 查找字符多次出现的位置处理
  17. 企业员工虚拟股权激励制度
  18. 算法设计与分析第一章递推算法
  19. memcpy函数实现(c语言)
  20. 战争与征服服务器维护,《战争与征服》12月5日停机维护公告

热门文章

  1. 倒数日电脑版_应用日报|iOS 或更名为 iPhoneOS,倒数日 Mac 版上线限时免费
  2. c#设计12星座速配软件_C#设计模式(12)——组合模式
  3. c语言中continue语句的作用是什么
  4. 设置数字范围的html语言,JavaScript奇技淫巧44招【实用】
  5. action mutation 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...
  6. mysql8 安装_MySQL8.x安装使用
  7. 在线画 有穷状态自动机 的软件_怎么画思维导图?不用下载软件,在线就能操作...
  8. 【渝粤教育】国家开放大学2018年春季 0599-22T工程造价管理基础理论与相关法规 参考试题
  9. 【渝粤教育】国家开放大学2018年春季 0283-21T广告创意与表现(二) 参考试题
  10. 【渝粤题库】国家开放大学2021春1374班级管理题目