滚雪球学Java(64):LinkedHashSet原理及实现解析

news2024/11/19 19:20:55

在这里插入图片描述

  咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~

在这里插入图片描述


🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!

环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8

文章目录

  • 前言
  • 摘要
  • LinkedHashSet
    • 概述
    • 源代码解析
    • 应用场景案例
    • 优缺点分析
    • 类代码方法介绍
    • 测试用例
      • 测试代码演示
      • 测试结果
      • 测试代码分析
    • 全文小结
  • 总结
    • 附录源码
  • ☀️建议/推荐你
  • 📣关于我

前言

  在Java开发中,使用集合类是必不可少的。其中,HashSet是最常用的集合类之一,但是HashSet在保证元素唯一性的同时,却不能保证插入顺序。因此,我们需要LinkedHashSet,在HashSet的基础上保证了元素插入的顺序。本文将对LinkedHashSet进行原理及实现的解析,帮助读者更好地理解LinkedHashSet。

摘要

  LinkedHashSet是HashSet和LinkedHashMap的结合体,它具有HashSet的高效查找和LinkedHashMap的有序性。LinkedHashSet底层使用的是LinkedHashMap实现,数据结构是一个双链表和一个哈希表。本文将深入剖析LinkedHashSet的实现原理和应用场景,帮助读者更好地理解该类的使用。

LinkedHashSet

概述

  LinkedHashSet是Java中的一个集合类,它实现了Set接口,继承了HashSet类,具有HashSet类的高效存储和检索元素的特点。同时,LinkedHashSet还对元素的插入顺序进行了维护,保证了元素的有序性,这是HashSet所不具备的。在实现上,LinkedHashSet底层使用的是LinkedHashMap,使用双向链表维护了元素的插入顺序。LinkedHashSet提供了以下构造方法:

public LinkedHashSet()
public LinkedHashSet(int initialCapacity)
public LinkedHashSet(int initialCapacity, float loadFactor)
public LinkedHashSet(Collection<? extends E> c)

  其中,第一个构造方法创建一个空的LinkedHashSet实例,第二个构造方法指定了初始化容量,第三个构造方法指定了初始化容量和加载因子,第四个构造方法使用指定集合来初始化LinkedHashSet。

源代码解析

  LinkedHashSet底层使用的是LinkedHashMap,它是一个基于散列表的Map实现。它的数据结构是一个双链表和一个哈希表。双链表用来维护元素的插入顺序,哈希表用来实现高效的存储和查找。

LinkedHashSet类的源代码如下:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = -2851667679971038690L;

    private final LinkedHashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    public LinkedHashSet() {
        map = new LinkedHashMap<>();
    }

    public LinkedHashSet(int initialCapacity) {
        super(initialCapacity);
        map = new LinkedHashMap<>(initialCapacity);
    }

    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

    public LinkedHashSet(Collection<? extends E> c) {
        map = new LinkedHashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

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

    public Object clone() {
        LinkedHashSet<E> clone = null;
        try {
            clone = (LinkedHashSet<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
        clone.map = (LinkedHashMap<E,Object>) map.clone();
        return clone;
    }

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        s.defaultWriteObject();
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());
        s.writeInt(map.size());
        for (E e : map.keySet())
            s.writeObject(e);
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        int capacity = s.readInt();
        float loadFactor = s.readFloat();
        int size = s.readInt();
        map = new LinkedHashMap<>(capacity, loadFactor);
        for (int i=0; i<size; i++) {
            E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
}

  可以看到,在LinkedHashSet的构造方法中,它使用LinkedHashMap来实现对元素插入顺序的维护。LinkedHashMap维护了一个双向链表,每次插入元素时,它会将新元素插入到链表的尾部,同时在哈希表中存储该元素的键值对,以便实现高效的存储和查找。LinkedHashSet中的add()方法就是通过调用LinkedHashMap中的put()方法实现插入新元素,而contains()方法则是直接调用LinkedHashMap的containsKey()方法实现对元素是否存在的判断。

  如下是部分源码截图:

在这里插入图片描述

  具体源码分析如下:
  LinkedHashSet是一个继承自HashSet的类,实现了Set接口,支持元素按照插入顺序进行遍历。它内部通过使用一个LinkedHashMap来维护元素插入顺序,实际上LinkedHashSet就是在HashSet的基础上利用了LinkedHashMap的有序性。

  LinkedHashSet提供了多个构造方法,可以仅传入初始容量、初始容量和负载因子等参数,也可以传入一个集合类对象进行初始化。

  LinkedHashSet的主要方法和HashSet相同,例如:contains(),add(),remove()等。因为继承自HashSet,所以LinkedHashSet也是基于哈希表实现的。其迭代器返回元素的顺序与元素插入顺序一致。

  LinkedHashSet的clone方法会创建一个新的LinkedHashSet对象,并将自身的LinkedHashMap进行浅复制。

  LinkedHashSet还实现了Serializable接口,在序列化和反序列化时,会将其内部的LinkedHashMap也一并进行序列化和反序列化。

在这里插入图片描述

应用场景案例

  LinkedHashSet在保证元素唯一性的同时,还保留了元素的插入顺序,因此,它在以下场景中得到了广泛应用:

  1. 缓存:当需要缓存一组数据时,如果希望缓存的数据按照插入顺序进行访问,就可以使用LinkedHashSet。

  2. 配置文件:当需要加载一组配置文件,并按照文件的出现顺序来访问它们的内容时,可以使用LinkedHashSet。

  3. 去重:当需要对一组数据进行去重,同时还需要保留它们的插入顺序时,可以使用LinkedHashSet。

优缺点分析

  LinkedHashSet的优点在于它继承了HashSet的高效性和LinkedHashMap的有序性,同时支持高效的元素查找、插入和删除等操作。其缺点在于它需要维护一个双向链表,因此,在内存使用方面可能会略微占用一些空间。

类代码方法介绍

  下面是LinkedHashSet类中的主要方法介绍:

  1. add(E e):向集合中添加元素,并返回添加成功与否的结果。

  2. remove(Object o):从集合中删除指定的元素,并返回删除结果。

  3. contains(Object o):判断集合中是否包含指定元素,如果包含则返回true,否则返回false。

  4. iterator():返回一个迭代器,用于遍历集合中的元素。

  5. size():返回集合中元素的数量。

  6. isEmpty():判断集合是否为空,如果为空则返回true,否则返回false。

测试用例

下面是使用LinkedHashSet的一个简单测试用例:

测试代码演示

package com.demo.javase.day64;

import java.util.LinkedHashSet;

/**
 * @Author bug菌
 * @Date 2023-11-06 11:36
 */
public class LinkedHashSetTest {

    public static void main(String[] args) {
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();

        // 向集合中添加元素
        linkedHashSet.add("A");
        linkedHashSet.add("B");
        linkedHashSet.add("C");
        linkedHashSet.add("D");
        linkedHashSet.add("E");

        // 遍历集合中的元素,并输出它们的顺序
        for (String str : linkedHashSet) {
            System.out.print(str + " ");
        }
        System.out.println();

        // 删除集合中的元素
        linkedHashSet.remove("C");

        // 再次遍历集合中的元素,并输出它们的顺序
        for (String str : linkedHashSet) {
            System.out.print(str + " ");
        }
        System.out.println();

        // 判断集合中是否包含指定元素
        System.out.println("集合中是否包含\"A\":" + linkedHashSet.contains("A"));
        System.out.println("集合中是否包含\"C\":" + linkedHashSet.contains("C"));
    }
}

测试结果

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

输出结果如下:

A B C D E 
A B D E 
集合中是否包含"A"true
集合中是否包含"C"false

在这里插入图片描述

测试代码分析

  根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。

  该代码演示了如何使用Java中的LinkedHashSet类。LinkedHashSet是HashSet的一个子类,它保留了元素插入的顺序。具体实现过程如下:

  1. 创建一个LinkedHashSet实例,该实例用于存储字符串类型的元素。

  2. 使用add方法向集合中添加元素。

  3. 使用for-each循环遍历集合中的元素,并输出它们的顺序。

  4. 使用remove方法删除集合中的元素。

  5. 再次使用for-each循环遍历集合中的元素,并输出它们的顺序。

  6. 使用contains方法判断集合中是否包含指定元素,并输出判断结果。

在实际开发中,LinkedHashSet通常用于需要保留元素插入顺序的场景,例如需要记录日志的应用程序等。由于LinkedHashSet实现了Set接口,因此它也具有Set接口的特性,即不能包含重复元素。

全文小结

  本篇文章介绍了LinkedHashSet的原理和实现。LinkedHashSet继承了HashSet的高效性和LinkedHashMap的有序性,可以在保证元素唯一性的同时,还保留了元素的插入顺序。底层使用LinkedHashMap实现,使用双向链表维护了元素的插入顺序。在应用场景上,LinkedHashSet常用于缓存、配置文件和去重等场景。其优点在于高效的元素查找、插入和删除等操作,而缺点在于需要维护一个双向链表,可能会略微占用一些空间。在使用上,LinkedHashSet提供了一系列方法,如add、remove、contains等,同时还具备Iterator迭代器用于遍历集合中的元素。最后,我们也给出了一个使用LinkedHashSet的简单测试用例。

总结

  本文介绍了LinkedHashSet的概念、原理和实现。LinkedHashSet是Java中的一个集合类,它继承了HashSet的高效性和LinkedHashMap的有序性,同时支持高效的元素查找、插入和删除等操作。在应用场景上,LinkedHashSet常用于缓存、配置文件和去重等场景。其优点在于保证元素唯一性的同时,还保留了元素的插入顺序,同时具备高效的操作。类方法包括add、remove、contains等,同时还具备Iterator迭代器用于遍历集合中的元素。对于想要深入理解Java集合框架的同学,学习LinkedHashSet是必不可少的一部分。

  …
  好啦,这期的内容就基本接近尾声啦,若你想学习更多,可以参考这篇专栏总结《「滚雪球学Java」教程导航帖》,本专栏致力打造最硬核 Java 零基础系列学习内容,🚀打造全网精品硬核专栏,带你直线超车;欢迎大家订阅持续学习。

附录源码

  如上涉及所有源码均已上传同步在「Gitee」,提供给同学们一对一参考学习,辅助你更迅速的掌握。

☀️建议/推荐你


  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学Java」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门Java编程,就像滚雪球一样,越滚越大,指数级提升。

  最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

  同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

📣关于我

  我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2.5 Windows驱动开发:DRIVER_OBJECT对象结构

在Windows内核中&#xff0c;每个设备驱动程序都需要一个DRIVER_OBJECT对象&#xff0c;该对象由系统创建并传递给驱动程序的DriverEntry函数。驱动程序使用此对象来注册与设备对象和其他系统对象的交互&#xff0c;并在操作系统需要与驱动程序进行交互时使用此对象。DRIVER_OB…

使用PHP编写采集药品官方数据的程序

目录 一、引言 二、程序设计和实现 1、确定采集目标 2、使用PHP的cURL库进行数据采集 3、解析JSON数据 4、数据处理和存储 5、数据验证和清理 6、数据输出和可视化 7、数据分析和挖掘 三、注意事项 1、合法性原则 2、准确性原则 3、完整性原则 4、隐私保护原则 …

Mac笔记本打开Outlook提示:您需要最新的版本的Outlook才能使用此数据库

Mac笔记本打开Outlook提示&#xff1a;您需要最新的版本的Outlook才能使用此数据库 故障现象&#xff1a; 卸载旧的office安装新版的office&#xff0c;打开outlook提示&#xff1a;您需要最新的版本的outlook才能使用此数据库。 故障截图&#xff1a; 故障原因&#xff1a;…

3类主流的车道检测AI模型

2014年的一天&#xff0c;我舒舒服服地躺在沙发上&#xff0c;看着我和加拿大朋友租的豪华滑雪别墅的篝火营地&#xff0c;突然&#xff0c;一个东西出现在我的视野里&#xff1a; “着火了&#xff01;着火了&#xff01;着火了&#xff01;” 我大喊。 几秒钟之内&#xff…

Redis 事务是什么?又和MySQL事务有什么区别?

目录 1. Redis 事务的概念 2. Redis 事务和 MySQL事务的区别&#xff1f; 3. Redis 事务常用命令 1. Redis 事务的概念 下面是在 Redis 官网上找到的关于事务的解释&#xff0c;这里划重点&#xff0c;一组命令&#xff0c;一个步骤。 也就是说&#xff0c;在客户端与 Redi…

synchronized jvm实现思考

底层实现时&#xff0c;为什么使用了cxq队列和entryList双向链表&#xff1f;这里为什么不跟AQS中使用一个队列就行了&#xff0c;加了一个entryList的目的是为了什么&#xff1f; 个人理解这里多一个entryList&#xff0c;可能是用于减少频繁的cas操作。假设存在很多锁竞争时&…

vue项目修改字体为苹方

vue项目修改字体为苹方 在项目assets中创建字体文件夹fonts&#xff0c;在文件夹中添加字体文件 在fonts底下创建css文件 font.css font-face {font-family: PingFang;src: url(./PingFang.ttf);font-weight: normal;font-style: normal; }需要在全局样式中引入&#xff0c;a…

网络安全准入技术之MAC VLAN

网络准入控制作为主要保障企业网络基础设施的安全的措施&#xff0c;特别是对于中大型企业来说&#xff0c;终端类型多样数量激增、终端管理任务重难度大、成本高。 在这样的一个大背景下&#xff0c;拥有更灵活的动态识别、认证、访问控制等成为了企业网络安全的最核心诉求之…

自定义GPT已经出现,并将影响人工智能的一切,做好被挑战的准备了吗?

原创 | 文 BFT机器人 OpenAI凭借最新突破&#xff1a;定制GPT站在创新的最前沿。预示着个性化数字协助的新时代到来&#xff0c;ChatGPT以前所未有的精度来满足个人需求和专业需求。 从本质上讲&#xff0c;自定义GPT是之前的ChatGPT的高度专业化版本或代理&#xff0c;但自定…

方阵的施密特正交化与相似对角化

方阵的施密特正交化与相似对角化 施密特正交化 施密特正交化步骤 example 略 相似对角化 相似对角化步骤 step1: step2: step3: step4: example 注:特征值的个数与秩无关 A {{-3, 6}, {-10, 6}}; Eigenvalues[A] V Eigenvectors[A]; P {V[[1]], V[[2]]}; P Transpo…

做一个Springboot文件上传-阿里云

概述 这个模块是用来上传头像以及文章封面的&#xff0c;图片的值是一个地址字符串&#xff0c;一般存放在本地或阿里云服务中 1、本地文件上传 我们将文件保存在一个本地的文件夹下&#xff0c;由于可能两个人上传不同图片但是却同名的图片&#xff0c;那么就会一个人的图片就…

ArkUI实战,深入浅出OpenHarmony应用开发

前言 | 《ArkUI实战》《ArkUI实战》深入浅出的介绍了OpenHarmony开发框架ArkUI组件的使用和应用开发流程&#xff0c;是OpenHarmony应用开发的必备电子书。https://www.arkui.club/ OpenHarmony开发资料归档__南先森-Laval社区OpenHarmony入门看这里 _南先森 Laval社区https:/…

Vue.js的生命周期钩子

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

DAY53 1143.最长公共子序列 + 1035.不相交的线 + 53. 最大子序和

1143.最长公共子序列 题目要求&#xff1a;给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08;也可以不删…

Android图片压缩插件

今天才发现这个还有插件&#xff0c;平时都是传网站上压缩完了又下载下来覆盖原文件。现在有这个了&#xff0c;开发好高效&#x1f601;&#xff01;分享给大家&#xff0c;可能对你们有用哈哈&#x1f606;。也可能你们早都知道了……

信息系统项目管理师(第四版)教材精读思维导图-第十五章到二十四章

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 ​ 思维导图源文件下载链接&#xff1a; 十五章风险管理 十六章采购管理 十七章干系人管理…

如何驾驭逻辑、形式逻辑与AI算法?

逻辑错误与逻辑形式错误是有区别的&#xff1a; 逻辑错误经常表现为没有逻辑因果&#xff0c;用辩证法、阴谋论和统计归纳替代因果演绎&#xff1b;而逻辑形式错误是&#xff1a;前提是形式和内容需要分离&#xff0c;就像数学与语文分开&#xff0c;数学代表形式&#xff0c;…

技术贴 | SQL 执行 - 执行器优化

本期技术贴主要介绍查询执行引擎的优化。查询执行引擎负责将 SQL 优化器生成的执行计划进行解释&#xff0c;通过任务调度执行从存储引擎里面把数据读取出来&#xff0c;计算出结果集&#xff0c;然后返回给客户。 在关系型数据库发展的早期&#xff0c;受制于计算机 IO 能力的…

SLF4J: Class path contains multiple SLF4J bindings.

问题截图 问题原因 这里就是由于hbase 安装路径下的一个文件和hadoop安装路径下的文件起冲突了 解决办法 我的路径&#xff1a; 这个一定要看自己电脑上的路径 /usr/local/hbase/lib/client-facing-thirdparty/ slf4j-log4j12-1.7.25.jar更名为&#xff1a; /usr/local/hb…

手把手带你学习 JavaScript 的 ES6 ~ ESn

文章目录 一、引言二、了解 ES6~ESn 的新特性三、掌握 ES6~ESn 的用法和实现原理四、深入挖掘和拓展《深入理解现代JavaScript》编辑推荐内容简介作者简介精彩书评目录 一、引言 JavaScript 是一种广泛使用的网络编程语言&#xff0c;它在前端开发中扮演着重要角色。随着时间的…