Java集合源码分析(九)——HashSet
简介
HashSet就是一个集合,里面不能有重复的元素,元素也是无序的。
 HashSet其实就是调用了HashMap实现的,所以,它也不是线程安全的。
 HashSet通过iterator()返回的迭代器是fail-fast的。
源码分析
由于HashSet的源码很短也很容易理解,这里就不再选取分析了。
package java.util;
import java.io.InvalidObjectException;
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
	// 内部的HashMap,后面的操作都是对它的调用
    private transient HashMap<E,Object> map;
	// 用于插入HashMap时的固定值
    private static final Object PRESENT = new Object();
	// 默认构造
    public HashSet() {
        map = new HashMap<>();
    }
	// 填入集合的构造
    public HashSet(Collection<? extends E> c) {
    	// 这里为什么需要在原来的容量和16中取最大值呢?看总结
        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);
    }
	// 采用LinkedHashMap来实现
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
	// 获取迭代器
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    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();
    }
	// 克隆函数,直接用map的clone函数,将原来的map克隆到新的map
    @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);
        }
    }
	// 将set输出为序列化
    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
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());
        // Write out size
        s.writeInt(map.size());
        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }
	// 将输入写入set
    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);
        }
        // 计算容量
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);
        // 创建一个新的内部map
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));
        // 依次写入集合元素
        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);
    }
}
总结
HashSet内部都是对HashMap的调用,不同的Key值,相同的value值。
 可以通过迭代器和for-each方法对其进行遍历。
为什么在初始化的时候有map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));?
- 首先HashMap的默认初始容量是16,如果填入集合的大小经过变换之后还小于默认初始容量,那么就直接使用默认初始容量。
- HashMap的默认加载因子就是0.75,当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,就会将容量翻倍,所以 c.size()/.75f) + 1 计算出来的正好是总的空间大小。
Java集合源码分析(九)——HashSet的更多相关文章
- java集合源码分析(三):ArrayList
		概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ... 
- java集合源码分析(六):HashMap
		概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ... 
- Java 集合源码分析(一)HashMap
		目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ... 
- Java集合源码分析(四)Vector<E>
		Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是 ... 
- Java集合源码分析(三)LinkedList
		LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ... 
- Java集合源码分析(二)ArrayList
		ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ... 
- java集合源码分析几篇文章
		java集合源码解析https://blog.csdn.net/ns_code/article/category/2362915 
- Java集合源码分析(五)HashSet<E>
		HashSet简介 HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素. HashSet源 ... 
- Java集合源码分析(六)TreeSet<E>
		TreeSet简介 TreeSet 是一个有序的集合,它的作用是提供有序的Set集合.它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, j ... 
随机推荐
- day05-类型转换和变量
			1.类型转换概念 java是强类型语言,所以有些运算的时候,需要用到类型转换 类型转换原则:低-->高,byte,short,char-->int-->long-->float ... 
- linux常用配置文件和命令总结
			常用配置文件说明: 1..设置-n永远生效:Vim的配置文件:命令模式想永久生效, ~/.vimrc,新建文件,在里面输入保存即可 2.设置别名永远生效:在~/.bashrc 修改当前用户家目录里的 ... 
- 高性能arm运行ceph存储基准测试
			关于arm 之前wdlab对外发布过一次约500个节点的arm的ceph集群,那个采用的是微集群的结构,使用的是双核的cortex-a9 ARM处理器,运行速度为1.3 GHz,内存为1 GB,直接焊 ... 
- Linux Shell 错误: $'\r': command not found错误解决
			在Linux下执行程序最省事的方式就是将系统的执行流程封装成一个shell脚本,上传到linux环境中后就可以直接执行了,但是今天在具体实施的时候出现了错误 $'\r': command not fo ... 
- 不是吧!做了两年java还没弄懂JVM堆?进来看看你就明白了
			堆的核心概述 一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域Java堆区在jvm启动的时候被创建,其空间大小也就确定了.是jvm管理的最大一块内存空间.(堆内存的大小可以调节)< ... 
- 巧妙运用Camtasia制作爱豆的动感影集
			对于追星族来说,收集爱豆的图片.视频是日常必做的事情,而对于进阶型的追星族来说,为爱豆自制各种精美的视频.影集等,会让自己追星之路显得更为充实. 我们可以借助Camtasia教程录制软件为爱豆制作各种 ... 
- 不想错过网课?不妨用Camtasia录制下来!
			2020年突发的这场疫情给我们的日常生活与学习带来了一些不便,却也意外的让网课走红了起来.小学.中学.大学都开始通过媒体工具或直播平台开始授课,但网络授课与实际课堂上课还是有区别的,学生们受到环境影响 ... 
- spring框架:(一) 技术说明(技术介绍,技术优势以及发展史等)
			一.技术说明(技术介绍,技术优势以及发展史等) 1.1.什么是spring 1.2.spring由来(发展历程) 1.3.spring核心 1.4.spring优点 1.5.spring体系结构 1. ... 
- LeetCode周赛#203 题解
			1561. 你可以获得的最大硬币数目 #贪心 题目链接 题意 有 3n 堆数目不一的硬币,你和你的朋友们打算按以下方式分硬币: 每一轮中,你将会选出 任意 3 堆硬币(不一定连续). Alice 将会 ... 
- 跟阿斌一起学鸿蒙(2). Ability vs App?
			在进一步实践之前,需要先弄明白一个概念:Ability. 不知道你有没有注意到,使用鸿蒙开发工具DevEco Studio创建项目时,我们选择创建的是一个个Ability. 这是为什么呢? 1. 鸿蒙 ... 
