在 Java 编程的世界里,JDK 源码犹如一座神秘的宝藏,其中的 Set 类更是我们日常开发中频繁使用的利器。今天,就让我们像勇敢的探险家一样,深入 JDK 源码,揭开 Set 类的神秘面纱,重点剖析适配器模式在其中的巧妙应用,看看它是如何让 Set 类焕发出独特魅力的!

一、Set 类:常用数据结构的重要角色

在我们的编程之旅中,Set 类就像一个收纳有序的工具箱,它具有不允许存储重复元素的特性,这使得它在处理数据去重、集合运算等场景中发挥着至关重要的作用。无论是处理用户输入数据、管理系统配置项,还是在复杂算法中进行数据筛选,Set 类都能助我们一臂之力。但你是否曾好奇,Set 类是如何在底层实现元素唯一性判断的呢?

二、适配器模式:Set 类背后的隐藏力量

(一)类图中的秘密

当我们审视 JDK 源码中与 Set 类相关的类图时,会惊喜地发现一个经典设计模式的身影——适配器模式。就像一位神奇的魔法师,它将 Map 接口的对象巧妙地包装成了 Set 接口,实现了两种不同接口之间的无缝转换。

(二)HashSet:适配器模式的实例展示

  1. 底层结构:依赖 HashMap



    在 HashSet 的源码中,我们发现它内部持有一个 transient 的 HashMap 实例:
private transient HashMap<E,Object> map;
// 用于与 HashMap 中元素关联的虚拟值
private static final Object PRESENT = new Object();

这个 HashMap 就是实现 Set 功能的关键“幕后英雄”。HashSet 巧妙地利用了 HashMap 的特性来实现自己的功能。

2. 添加元素:委托给 HashMap

当我们调用 HashSet 的 add 方法时,实际上是将元素作为 HashMap 的键,而那个固定的 PRESENT 对象作为值,存入了 HashMap 中:

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

这样一来,通过 HashMap 键的唯一性,就轻松保证了 Set 中元素的唯一性。当我们向 HashSet 中添加元素时,就如同将物品放入一个经过特殊改造的 HashMap 容器中,它会自动帮我们去重。

3. 迭代器:获取 Map 的键迭代器

而当我们获取 HashSet 的迭代器时,它直接返回的是 HashMap 的键集合的迭代器:

public Iterator<E> iterator() {
return map.keySet().iterator();
}

这使得我们在遍历 HashSet 时,实际上是在遍历 HashMap 的键,从而获取到 Set 中的所有元素。

(三)LinkedHashSet:类似的适配逻辑

LinkedHashSet 的实现也别具匠心。它在构造函数中调用了父类的构造函数,最终创建了一个 LinkedHashMap:

public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
// 其他构造函数类似,最终都会调用到父类构造函数创建 LinkedHashMap

其构造函数最终会调用到类似这样的父类构造函数:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

LinkedHashSet 利用 LinkedHashMap 的有序特性,不仅实现了元素的唯一性,还能保持元素插入的顺序,为我们提供了一种有序的 Set 实现。

(四)TreeSet:适配 NavigableMap

TreeSet 的实现同样依赖于适配器模式,它内部持有一个 transient 的 NavigableMap 实例:

private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();

TreeSet 通过将元素存储在 NavigableMap 中,并利用其排序功能,实现了对元素的有序存储和操作,为我们提供了一个有序且不重复的 Set 集合。

三、不仅仅是适配器模式:桥接模式的影子

有趣的是,在分析 JDK 源码中 Set 类的实现时,我们还能发现一些类似桥接模式的变形痕迹。虽然从本质上来说,这里的实现更符合适配器模式的定义,但这种相似性也让我们对设计模式的运用有了更深入的思考。它就像一座横跨在不同设计理念之间的桥梁,让我们看到了在实际编程中,设计模式之间可能存在的微妙联系和灵活转换。

四、总结与启示:源码学习的价值

通过深入剖析 JDK 源码中 Set 类对适配器模式的应用,我们不仅揭开了 Set 类高效实现元素唯一性判断和操作的神秘面纱,更深刻体会到了设计模式在优化代码结构、提高代码复用性和灵活性方面的巨大威力。这就像在黑暗中点亮了一盏明灯,为我们今后的编程实践指明了方向。

在日常编程中,我们不应仅仅满足于使用现成的类和接口,更应该像这样深入源码,学习大师们的设计思路和技巧。只有这样,我们才能不断提升自己的编程水平,打造出更加高效、健壮、优雅的软件系统。希望今天的探索能成为你编程之路上的宝贵财富,让我们一起在源码的海洋中继续畅游,发现更多的智慧结晶!

作者:代老师的编程课

出处:https://zthinker.com/

如果你喜欢本文,请长按二维码,关注 Java码界探秘

.

Java设计模式——适配器模式的精妙应用:探秘 JDK 源码中的 Set 类的更多相关文章

  1. 简单工厂(三)——JDK源码中的简单工厂

    private static Calendar createCalendar(TimeZone zone,Locale aLocale) { CalendarProvider provider = L ...

  2. JDK源码中,都有哪些NB的设计模式?

    转载:https://mp.weixin.qq.com/s/h88UxB9F2MkTbHqck3KQiQ 一.结构性模式: 1.适配器模式: 常用于将一个新接口适配旧接口 肥朝小声逼逼:在我们业务代码 ...

  3. Java1.8 JDK源码中,对两个类进行 按位与 操作是什么意思

    Java容器类库中的Map接口(java\util\Map.java)中有一个Entry接口(java\util\Map.java),其中有几个接口方法用到了类和类的按位与操作,即类和类之间有 &am ...

  4. JDK源码中使用的设计模式

    结构型模式: 适配器模式: 用来把一个接口转化成另一个接口. java.util.Arrays#asList() javax.swing.JTable(TableModel) java.io.Inpu ...

  5. JDK源码阅读:Object类阅读笔记

    Object 1. @HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate public final native Class<?> g ...

  6. JDK源码中的英文注释翻译(Enum<E extends Enum<E>>)

    public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializab ...

  7. JDK源码中的英文注释翻译(Class)

    public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, Annotat ...

  8. JDK源码阅读:String类阅读笔记

    String public final class String implements java.io.Serializable, Comparable<String>, CharSequ ...

  9. JDK源码那些事儿之浅析Thread上篇

    JAVA中多线程的操作对于初学者而言是比较难理解的,其实联想到底层操作系统时我们可能会稍微明白些,对于程序而言最终都是硬件上运行二进制指令,然而,这些又太过底层,今天来看一下JAVA中的线程,浅析JD ...

  10. Java设计模式:23种设计模式全面解析(超级详细)以及在源码中的应用

    从网络上找的设计模式, 很全面,只要把UML类图看懂了, 照着类图将代码实现是很容易的事情. 步骤: 先看懂类图, 然后将代码实现, 之后再看文字 http://c.biancheng.net/des ...

随机推荐

  1. 【笔记】Tapable源码解析图以及webpack怎样实现一个插件plugin

    Tapable源码解析图,如图所示: 一个webpack plugin由一下几个步骤组成: 一个JavaScript类函数. 在函数原型 (prototype)中定义一个注入compiler对象的ap ...

  2. [34](CSP 集训)CSP-S 联训模拟 1

    A 几何 重复若干次 -> 不能重叠,因此考虑直接暴力 DP 设 \(f_{i,j,k}\) 表示主串匹配到第 \(i\) 位(将前 \(i\) 位分别归为两类),其中 \(x\) 在重复了若干 ...

  3. 第43天:WEB攻防-PHP应用&SQL注入&符号拼接&请求方法&HTTP头&JSON&编码类

    #PHP-MYSQL-数据请求类型 SQL语句由于在黑盒中是无法预知写法的,SQL注入能发成功是需要拼接原SQL语句,大部分黑盒能做的就是分析后各种尝试去判断,所以有可能有注入但可能出现无法注入成功的 ...

  4. push_back和 emplace_back背后的逻辑

    push_back 与 emplace_back 的区别 push_back: 功能:将一个对象(或其副本)添加到 vector 的末尾. 参数:接受一个对象(或其副本)的引用. 过程: 如果传入的是 ...

  5. 推荐一款轻量级 eBPF 前端工具 ply

    1 Overview ply 是 eBPF 的 front-end 前端工具之一,专为 embedded Linux systems 开发,采用 C 语言编写,只需 libc 和内核支持 BPF 就可 ...

  6. vue前端开发仿钉图系列(2)左侧图层列表的开发详解

    项目开发前还是特别说明一下组件库的重要性,谢谢饿了么团队分享的element组件库,大大节省了页面的开发成本.左侧图层列表核心功能有1.根据图层类型展示点线面2.开关控制右侧地图上点线面的展示和隐藏3 ...

  7. iOS开发Block使用详解

    项目开发中经常会用到页面之间传值的问题,常用的方法是通知.单例.代理.block等.最近项目忙完,有空细细研究了一下block的用法,收货多多. block又称闭包,它的实现原理是c语言的函数指针.函 ...

  8. iOS键盘通知弹框使用小结

    项目开发中文本框输入的时候经常会用到键盘弹框遮挡的问题.解决办法就是根据底部键盘弹出的高度动态的改变对应view的位置.这里以多行文本框输入为例,效果图如下. //第一步,注册监听键盘通知 [[NSN ...

  9. 键盘事件 key keyCode

    keyCode 8 = BackSpace BackSpace keyCode 9 = Tab Tab keyCode 12 = Clear keyCode 13 = Enter keyCode 16 ...

  10. 最详细CentOS7.6安装openGauss5.0.3教程

    一.环境准备 1.1 主机信息 项目 内容 操作系统 CentOS7.6 IP 192.168.4.201 主机名 opgs201 CPU 8core 内存 16GB 磁盘1 100GB 1.2 操作 ...