基于HashMap实现
HashSet内部实现是基于HashMap的,这是阅读和理解HashSet源码的关键,只要理解了HashMap的实现原理,再来看HashSet的源码就简单了。对于HashMap源码的阅读可以看之前的一篇文章。
首先看两个重要的成员变量:
private transient HashMap<E,Object> map; private static final Object PRESENT = new Object();//HashMap的value存放的都是这个对象
HashSet内部的主要成员变量是一个HashMap,使用这个HashMap的key进行数据存放(利用HashMap的key的不重复,保证HashSet的不可重复),HashMap的value对HashSet没啥意义,所以统一存放的同一个对象PRESENT的引用。
构造方法
HashSet的构造方法的目的只有一个:实例化自己的HashMap成员变量(实际上就是调用HashMap的各个构造方法进行实例化)。
默认构造方法:
public HashSet() {//默认构造方法,实际上调用的HashMap的默认构造方法进行实例化 map = new HashMap<>(); }
指定容量的构造方法:
public HashSet(int initialCapacity) {//调用HashMap的指定容量构造方法 map = new HashMap<>(initialCapacity); }
指定容量和增长因子构造方法:
public HashSet(int initialCapacity, float loadFactor) {//调用HashMap的指定容量和增长因子构造方法 map = new HashMap<>(initialCapacity, loadFactor); }
参数为集合的构造方法
public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));//根据集合的大小,调用HashMap的指定容量和增长因子构造方法 addAll(c); 实际上循环调用的是HashSet的add方法 }
包保护的构造方法
HashSet(int initialCapacity, float loadFactor, boolean dummy) {//该构造方法普通用户无法使用 map = new LinkedHashMap<>(initialCapacity, loadFactor); }
这个构造方法只跟HashSet同一个包下的类才可以见,即:java.util包中类才有权使用。目前发现只有在LinkedHashSet的构造方法中调用过该方法,LinkedHashSet是HashSet的子类。
其他主要方法:
HashSet的逻辑处理方法,都是直接调用HashMap的相关方法进行处理。比如add方法调用的HashMap的put方法,remove方法调用的HashMap的remove 等等:
public boolean add(E e) { return map.put(e, PRESENT)==null; //hashMap中的每个value都是存放的同一个PRESENT对象的引用 } public boolean remove(Object o) { return map.remove(o)==PRESENT; //调用的HashMap的remove方法 }
其他方法不再一一列举,都是直接调用HashMap的相对应方法。
接下来看看两个特殊的方法。
writeObject 和readObject方法
HashSet实现了Serializable序列化接口,我们都知道实现了该接口其实就是可序列化的。重写writeObject 和readObject方法可以实现自定义序列化。
/** * 重写的序列化方法 * @param s * @throws java.io.IOException */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // 调用默认序列化方法 s.defaultWriteObject(); // 序列化HashMap的容量和增长因子 s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); // 序列化HashMap的大小 s.writeInt(map.size()); // 序列化HashSet的每一个节点 for (E e : map.keySet()) s.writeObject(e); } } /** * 重写反序列化方法 * @param s * @throws java.io.IOException * @throws ClassNotFoundException */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // 调用默认的序列化方法 s.defaultReadObject(); // 反序列化容量 int capacity = s.readInt(); if (capacity < 0) { throw new InvalidObjectException("Illegal capacity: " + capacity); } // 反序列化增长因子. float loadFactor = s.readFloat(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) { throw new InvalidObjectException("Illegal load factor: " + loadFactor); } // 反序列化大小. int size = s.readInt(); if (size < 0) { throw new InvalidObjectException("Illegal size: " + size); } // Set the capacity according to the size and load factor ensuring that // the HashMap is at least 25% full but clamping to maximum capacity. capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f), HashMap.MAXIMUM_CAPACITY); // 根据以上参数,构造新的HashMap map = (((HashSet<?>)this) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); // 反序列化每个节点,并放入HashMap for (int i=0; i<size; i++) { @SuppressWarnings("unchecked") E e = (E) s.readObject(); map.put(e, PRESENT); } }
我们再看下HashSet的HashMap成员变量定义:private transient HashMap<E,Object> map; transient修饰是不能被序列化的,这里所谓的不能被序列化 是指不能被默认的序列化方法序列化。但是HashSet采用重写writeObject 和readObject方法,自己对transient成员变量进行序列化和反序列化。
为什么HashSet要自定义序列化规则呢?个人理解:可以从源码上来看,序列化和反序列化里不同的地方,就是HashMap的容量capacity在反序列化时进行了重写计算。由于HashMap的容量是自动增长的,为了避免反序列化后重新生成HashMap空间浪费,需要重新进行容量计算。
其实ArrayList、HashMap、HashSet等他们的主要成员变量都是transien修饰的,都重写了writeObject 和readObject方法,重写的方式都类似(重新计算容量)。这些类都有一个共同的特征,就是容量会自动增长。
当然这只是表面上的理解,其实默认的序列化会遍历整个对象的拓扑关系,会消耗更多的空间和时间,自己实现的序列化方法则会简单得多。
关于java的序列化和反序列化,且听下回分解。
相关推荐
HashSet作为Java集合框架中一个重要的非同步集合实现,它在JDK 7.0中的底层实现原理是基于HashMap来存储和操作数据的。下面就详细介绍HashSet的实现原理。 首先,HashSet是Set接口的一个实现类,它用于存储唯一性的...
Java Development Kit (JDK) 源码是学习和理解Java平台核心机制的关键资源。...通过深入阅读JDK源码,开发者不仅可以增强对Java语言特性的理解,还能提高解决实际问题的能力,这对于成为一名优秀的Java开发者至关重要。
通过阅读和分析JDK1.8的源码,开发者不仅可以深入了解Java语言的实现,还能学习到优秀的编程实践和设计模式,为日常开发工作提供强大支持。同时,对于优化代码性能、解决并发问题以及深入理解Java生态系统有着不可...
阅读JDK源码是提升JAVA技术的关键步骤,因为它揭示了Java平台的基础构造和设计理念。JDK1.8源码包含了众多重要的API,如IO框架、集合框架和并发框架等,这些都是Java开发者日常工作中不可或缺的部分。下面,我们将...
Java开发工具包(JDK)是Java编程语言的核心组件,其中包含了运行和开发Java应用程序所需的库和工具。在 JDK 1.8 版本中,`rt.jar` 是一个非常重要的文件,它包含了Java标准版(Java SE)的运行时类库。这个库包含了...
【文件名称列表】: jdk-code-any-master 这个文件名可能指的是一个包含JDK源码分析项目的Git仓库,"master"分支通常代表主分支,包含了项目的最新稳定版本。通过这样的仓库,开发者可以获取到源码、阅读注释、跟踪...
《深入解析JDK1.8源码:开启Java编程新视野》 JDK1.8是Java发展历程中的一个重要里程碑,它的出现带来了许多创新特性和优化,极大地提升了开发效率和程序性能。尤其值得一提的是,它引入了Lambda表达式、Stream API...
jdk源码学习 JavaSourceLearn 版本号 版本 corretto-1.8.0_275 方式 逐步阅读源码添加注释、notes文件夹添加笔记 计划学习任务计划 标题为包名,后面序号为优先级1-4,优先级递减 java.lang Object 1 String 1 ...
Java JDK源码是Java开发工具包的原始代码,对于深入理解Java语言的工作原理以及进行高级编程和优化至关重要。源码包含了Java平台的核心类库,如`java.lang`, `java.util`, `java.io`, `java.nio`等,这些库提供了...
**JDK 1.6 源码详解** JDK(Java Development Kit)1.6,也被称为Java SE 6,是Oracle公司发布的Java平台标准版的一个重要版本。这个版本的发布为Java开发者提供了大量的新特性和改进,使得Java编程更加高效、安全...
Java源码是深入理解Java平台工作原理的关键,JDK源码包含了Java开发工具集的核心实现。通过对JDK源码的学习,开发者可以了解到Java语言的底层机制,提升编程技能,更好地解决实际问题。以下将详细探讨Java源代码和...
在Java编程领域,深入理解JDK源码是提升技能的关键步骤。"JavaSourceLearn"项目致力于帮助开发者探索和学习JDK的源代码,这将使我们能够更好地理解Java语言的工作原理,提高问题解决能力,以及优化代码性能。下面,...
Java JDK8源码是Java开发人员深入理解Java平台工作原理的重要资源。对于任何希望提升Java技术水平,特别是对性能优化、并发编程或者想要成为一名更高级的Java开发者的人来说,研究JDK源码是不可或缺的一环。在这里,...
**JDK源码详解** Java Development Kit (JDK) 是Java编程语言的核心组成部分,它包含了编译、运行Java程序所需的所有工具和库。在给定的压缩包“JDK_AIP1.7源码.rar”中,我们主要关注的是JDK 1.7版本的源代码,这...
通过阅读和实践这些源码,开发者不仅能加深对Java JDK的理解,还能提高解决实际问题的能力。无论是初学者还是有经验的开发者,都能从中受益匪浅。这份“JAVA JDK实例开发宝典源码”无疑是一份珍贵的学习资料,值得每...
Java JDK实例宝典源码是Java开发者的重要参考资料,它涵盖了JDK中的各种核心类库、API及其实现的源代码。这些源码对于深入理解Java语言的底层运作机制、优化代码以及解决实际问题有着不可估量的价值。下面,我们将...
《深入解析JDK1.8源码》 JDK1.8是Java开发的一个重要版本,它引入了许多新的特性和优化,对开发者来说,理解其源码有助于提升编程技巧和解决问题的能力。本篇将深入探讨JDK1.8中的一些核心组件和关键改进。 一、...
java jdk1.8 源码 Java-source-reading 缓慢更新一些个人学习java相关源码过程中的笔记,在这里你将不可避免地看到以下情况: 个别不懂/没想好的地方留空待补全 限于个人水平出现的解读错误 ...HashSet LinkedHashMap
对于初学者,可以先从简单的类开始,如`String`和`ArrayList`,然后逐渐深入到更复杂的类,如`Thread`和`ConcurrentHashMap`。了解源码不仅可以提高编程效率,还能让你在遇到问题时,能够快速定位和解决问题。 总之...
"JDK-learning"项目聚焦于对JDK的部分源码进行学习和分析,旨在帮助开发者更好地了解Java运行机制,从而在实际工作中能够更加得心应手。 【描述】"JDK学习主要涉及部分源码的学习,这部分学习涵盖了JDK的核心组件和...