`

HashSet--阅读源码从jdk开始

阅读更多

基于HashMap实现

 

HashSet内部实现是基于HashMap的,这是阅读和理解HashSet源码的关键,只要理解了HashMap的实现原理,再来看HashSet的源码就简单了。对于HashMap源码的阅读可以看之前的一篇文章

 

首先看两个重要的成员变量:

private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();//HashMap的value存放的都是这个对象

 

HashSet内部的主要成员变量是一个HashMap,使用这个HashMapkey进行数据存放(利用HashMapkey的不重复,保证HashSet的不可重复),HashMapvalueHashSet没啥意义,所以统一存放的同一个对象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方法调用的HashMapput方法,remove方法调用的HashMapremove 等等:

 

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);
        }
}

 

 

我们再看下HashSetHashMap成员变量定义:private transient HashMap<E,Object> map; transient修饰是不能被序列化的,这里所谓的不能被序列化 是指不能被默认的序列化方法序列化。但是HashSet采用重写writeObject readObject方法,自己对transient成员变量进行序列化和反序列化。

为什么HashSet要自定义序列化规则呢?个人理解:可以从源码上来看,序列化和反序列化里不同的地方,就是HashMap的容量capacity在反序列化时进行了重写计算。由于HashMap的容量是自动增长的,为了避免反序列化后重新生成HashMap空间浪费,需要重新进行容量计算。

 

其实ArrayListHashMapHashSet等他们的主要成员变量都是transien修饰的,都重写了writeObject readObject方法,重写的方式都类似(重新计算容量)。这些类都有一个共同的特征,就是容量会自动增长。

 

当然这只是表面上的理解,其实默认的序列化会遍历整个对象的拓扑关系,会消耗更多的空间和时间,自己实现的序列化方法则会简单得多。

 

关于java的序列化和反序列化,且听下回分解。

0
1
分享到:
评论

相关推荐

    源码解析jdk7.0集合:HashSet的底层实现原理.pdf

    HashSet作为Java集合框架中一个重要的非同步集合实现,它在JDK 7.0中的底层实现原理是基于HashMap来存储和操作数据的。下面就详细介绍HashSet的实现原理。 首先,HashSet是Set接口的一个实现类,它用于存储唯一性的...

    jdk源码 jdk源码

    Java Development Kit (JDK) 源码是学习和理解Java平台核心机制的关键资源。...通过深入阅读JDK源码,开发者不仅可以增强对Java语言特性的理解,还能提高解决实际问题的能力,这对于成为一名优秀的Java开发者至关重要。

    JDK源码,整合所有内容

    通过阅读和分析JDK1.8的源码,开发者不仅可以深入了解Java语言的实现,还能学习到优秀的编程实践和设计模式,为日常开发工作提供强大支持。同时,对于优化代码性能、解决并发问题以及深入理解Java生态系统有着不可...

    jdk源码阅读.zip

    阅读JDK源码是提升JAVA技术的关键步骤,因为它揭示了Java平台的基础构造和设计理念。JDK1.8源码包含了众多重要的API,如IO框架、集合框架和并发框架等,这些都是Java开发者日常工作中不可或缺的部分。下面,我们将...

    jdk1.8 rt.jar 源码

    Java开发工具包(JDK)是Java编程语言的核心组件,其中包含了运行和开发Java应用程序所需的库和工具。在 JDK 1.8 版本中,`rt.jar` 是一个非常重要的文件,它包含了Java标准版(Java SE)的运行时类库。这个库包含了...

    jdk-code-any:jdk源码分析

    【文件名称列表】: jdk-code-any-master 这个文件名可能指的是一个包含JDK源码分析项目的Git仓库,"master"分支通常代表主分支,包含了项目的最新稳定版本。通过这样的仓库,开发者可以获取到源码、阅读注释、跟踪...

    JDK1.8源码完整版

    《深入解析JDK1.8源码:开启Java编程新视野》 JDK1.8是Java发展历程中的一个重要里程碑,它的出现带来了许多创新特性和优化,极大地提升了开发效率和程序性能。尤其值得一提的是,它引入了Lambda表达式、Stream API...

    javajdk源码学习-JavaSourceLearn:JDK源码学习

    jdk源码学习 JavaSourceLearn 版本号 版本 corretto-1.8.0_275 方式 逐步阅读源码添加注释、notes文件夹添加笔记 计划学习任务计划 标题为包名,后面序号为优先级1-4,优先级递减 java.lang Object 1 String 1 ...

    javajdk源码-JDK_SourceCode:jdk

    Java JDK源码是Java开发工具包的原始代码,对于深入理解Java语言的工作原理以及进行高级编程和优化至关重要。源码包含了Java平台的核心类库,如`java.lang`, `java.util`, `java.io`, `java.nio`等,这些库提供了...

    JDK1.6源码

    **JDK 1.6 源码详解** JDK(Java Development Kit)1.6,也被称为Java SE 6,是Oracle公司发布的Java平台标准版的一个重要版本。这个版本的发布为Java开发者提供了大量的新特性和改进,使得Java编程更加高效、安全...

    java源码之jdk源码

    Java源码是深入理解Java平台工作原理的关键,JDK源码包含了Java开发工具集的核心实现。通过对JDK源码的学习,开发者可以了解到Java语言的底层机制,提升编程技能,更好地解决实际问题。以下将详细探讨Java源代码和...

    javajdk源码学习-JavaSourceLearn:jdk源码学习

    在Java编程领域,深入理解JDK源码是提升技能的关键步骤。"JavaSourceLearn"项目致力于帮助开发者探索和学习JDK的源代码,这将使我们能够更好地理解Java语言的工作原理,提高问题解决能力,以及优化代码性能。下面,...

    java_jdk8源码

    Java JDK8源码是Java开发人员深入理解Java平台工作原理的重要资源。对于任何希望提升Java技术水平,特别是对性能优化、并发编程或者想要成为一名更高级的Java开发者的人来说,研究JDK源码是不可或缺的一环。在这里,...

    JDK_AIP1.7源码.rar

    **JDK源码详解** Java Development Kit (JDK) 是Java编程语言的核心组成部分,它包含了编译、运行Java程序所需的所有工具和库。在给定的压缩包“JDK_AIP1.7源码.rar”中,我们主要关注的是JDK 1.7版本的源代码,这...

    JAVA JDK实例开发宝典源码

    通过阅读和实践这些源码,开发者不仅能加深对Java JDK的理解,还能提高解决实际问题的能力。无论是初学者还是有经验的开发者,都能从中受益匪浅。这份“JAVA JDK实例开发宝典源码”无疑是一份珍贵的学习资料,值得每...

    java jdk 实例宝典源码

    Java JDK实例宝典源码是Java开发者的重要参考资料,它涵盖了JDK中的各种核心类库、API及其实现的源代码。这些源码对于深入理解Java语言的底层运作机制、优化代码以及解决实际问题有着不可估量的价值。下面,我们将...

    jdk1.8源码

    《深入解析JDK1.8源码》 JDK1.8是Java开发的一个重要版本,它引入了许多新的特性和优化,对开发者来说,理解其源码有助于提升编程技巧和解决问题的能力。本篇将深入探讨JDK1.8中的一些核心组件和关键改进。 一、...

    javajdk1.8源码-Java-source-reading:jdk1.8源代码分析

    java jdk1.8 源码 Java-source-reading 缓慢更新一些个人学习java相关源码过程中的笔记,在这里你将不可避免地看到以下情况: 个别不懂/没想好的地方留空待补全 限于个人水平出现的解读错误 ...HashSet LinkedHashMap

    jdk src 源码 压缩包提取

    对于初学者,可以先从简单的类开始,如`String`和`ArrayList`,然后逐渐深入到更复杂的类,如`Thread`和`ConcurrentHashMap`。了解源码不仅可以提高编程效率,还能让你在遇到问题时,能够快速定位和解决问题。 总之...

    jdk-learning:部分jdk源码学习

    "JDK-learning"项目聚焦于对JDK的部分源码进行学习和分析,旨在帮助开发者更好地了解Java运行机制,从而在实际工作中能够更加得心应手。 【描述】"JDK学习主要涉及部分源码的学习,这部分学习涵盖了JDK的核心组件和...

Global site tag (gtag.js) - Google Analytics