一、JDK 1.8 中 TreeSet 的 add 方法源码详细分析

TreeSet 是 Java 集合框架中的一个有序集合类,基于红黑树(TreeMap)实现。TreeSet 的 add 方法用于向集合中添加元素。如果元素已存在,则不会添加并返回 false;如果元素不存在,则添加并返回 true。下面我们将从 TreeSet 的 add 方法入手,逐步分析其源码实现

二、TreeSet 的 add 方法

TreeSet 的 add 方法源码如下:

参数说明

  • e:要添加的元素。

  • PRESENT:一个固定的 Object 对象,作为 TreeMap 的值(value)

返回值

  • 如果元素 e 不存在,map.put(e, PRESENT) 返回 null,表示添加成功

  • 如果元素 e 已存在,map.put(e, PRESENT) 返回 PRESENT,表示添加失败

三、TreeSet 的内部实现

TreeSet 的内部是基于 TreeMap 实现的。TreeSet 的元素作为 TreeMap 的键(key),而值(value)是一个固定的 PRESENT 对象(private static final Object PRESENT = new Object())

TreeSet 的构造函数

TreeSet 的构造函数会初始化内部的 TreeMap。例如:

PRESENT 对象

PRESENT 是一个静态常量,用于作为 TreeMap 的值:

四、TreeMap 的 put 方法

TreeSet 的 add 方法依赖于 TreeMap 的 put 方法。以下是 TreeMap 的 put 方法的源码(JDK 1.8):

    public V put(K key, V value) {
Entry<K,V> t = root; // 获取根节点
if (t == null) { // 如果树为空
compare(key, key); // 检查键是否为 null
root = new Entry<>(key, value, null); // 创建根节点
size = 1; // 大小设置为 1
modCount++; // 修改计数器加 1
return null; // 返回 null,表示添加成功
}
int cmp;
Entry<K,V> parent;
Comparator<? super K> cpr = comparator; // 获取比较器
if (cpr != null) { // 如果使用自定义比较器
do {
parent = t;
cmp = cpr.compare(key, t.key); // 比较键
if (cmp < 0) // 如果键小于当前节点
t = t.left; // 移动到左子树
else if (cmp > 0) // 如果键大于当前节点
t = t.right; // 移动到右子树
else // 如果键等于当前节点
return t.setValue(value); // 更新值并返回旧值
} while (t != null);
} else { // 如果使用自然顺序
if (key == null) // 如果键为 null
throw new NullPointerException(); // 抛出异常
Comparable<? super K> k = (Comparable<? super K>) key; // 强制转换为 Comparable
do {
parent = t;
cmp = k.compareTo(t.key); // 比较键
if (cmp < 0) // 如果键小于当前节点
t = t.left; // 移动到左子树
else if (cmp > 0) // 如果键大于当前节点
t = t.right; // 移动到右子树
else // 如果键等于当前节点
return t.setValue(value); // 更新值并返回旧值
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent); // 创建新节点
if (cmp < 0) // 如果键小于父节点
parent.left = e; // 将新节点作为左子节点
else // 如果键大于父节点
parent.right = e; // 将新节点作为右子节点
fixAfterInsertion(e); // 修复红黑树的性质
size++; // 大小加 1
modCount++; // 修改计数器加 1
return null; // 返回 null,表示添加成功
}

五、源码解析

1、检查树是否为空:

  • 如果树为空,创建根节点并返回 null

2、比较键:

  • 如果使用自定义比较器,调用 compare 方法比较键

  • 如果使用自然顺序,调用 compareTo 方法比较键

3、查找插入位置:

  • 根据比较结果,移动到左子树或右子树

  • 如果找到相同的键,更新值并返回旧值

4、插入新节点:

  • 创建新节点,并根据比较结果将其作为左子节点或右子节点

5、修复红黑树的性质:

  • 调用 fixAfterInsertion 方法修复红黑树的性质(如颜色调整和旋转)

6、更新大小和修改计数器:

  • 大小加 1

  • 修改计数器加 1

六、总结

TreeSet 的 add 方法通过调用内部 TreeMap 的 put 方法实现元素的添加。它的核心逻辑是:

  • 如果元素不存在,添加到 TreeMap 中并返回 true

  • 如果元素已存在,返回 false

在 JDK 1.8 中,TreeMap 的实现基于红黑树,保证插入、删除和查找操作的时间复杂度为 O(log n)。TreeSet 的 add 方法适合需要有序集合的场景。在使用时,注意实现 Comparable 接口或提供 Comparator,并根据需求选择合适的集合类

TreeSet的add方法源码分析的更多相关文章

  1. TreeSet集合的add()方法源码解析(01.Integer自然排序)

    >TreeSet集合使用实例 >TreeSet集合的红黑树 存储与取出(图) >TreeSet的add()方法源码     TreeSet集合使用实例 package cn.itca ...

  2. Java split方法源码分析

    Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...

  3. invalidate和requestLayout方法源码分析

    invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewG ...

  4. Linq分组操作之GroupBy,GroupJoin扩展方法源码分析

    Linq分组操作之GroupBy,GroupJoin扩展方法源码分析 一. GroupBy 解释: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值. 查询表达式: var ...

  5. jQuery实现DOM加载方法源码分析

    传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... }  但 ...

  6. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  7. jQuery.extend()方法和jQuery.fn.extend()方法源码分析

    这两个方法用的是相同的代码,一个用于给jQuery对象或者普通对象合并属性和方法一个是针对jQuery对象的实例,对于基本用法举几个例子: html代码如下: <!doctype html> ...

  8. jQuery.clean()方法源码分析(一)

    在jQuery 1.7.1中调用jQuery.clean()方法的地方有三处,第一次就是在我之前的随笔分析jQuery.buildFramgment()方法里面的,其实还是构造函数的一部分,在处理诸如 ...

  9. ArrayList方法源码分析

    本文将从ArrayList类的存储结构.初始化.增删数据.扩容处理以及元素迭代等几个方面,分析该类常用方法的源码. 数据存储设计 该类用一个Object类型的数组存储容器的元素.对于容量为空的情况,提 ...

  10. TreeSet与TreeMap的源码分析 JDK7

    TreeSet存储原则是:不可重复,有序的. public TreeSet() { this(new TreeMap<E,Object>()); } public TreeSet(Comp ...

随机推荐

  1. NPOI与excelcnv.exe

    在使用NPOI解析一些比较古老的仪器生成的excel文件时,经常会这个错误:The supplied spreadsheet seems to be Excel 5.0/7.0 (BIFF5) for ...

  2. Hetao P1169 点集 题解 [ 黄 ][ 线性dp ]

    点集 题解 一道非常傻逼,非常傻逼的暴力题,一点都不优雅,这能放普及 T4 真是开了眼了. 本题难点主要就是在时间复杂度的计算上,只要算对了并且有勇气去打就能 AC . 首先发现能形成一个点集,当且仅 ...

  3. Tensorflow 安装和测试(Anaconda4.7.10+windows10)

    一. 软件下载 二. 配置相关 1. 修改 Jupyter notebook 默认工作路径 (1)打开 Anaconda Prompt ,输入 jupyter notebook --generate- ...

  4. Linux命令行连接蓝牙设备

    Linux命令行连接蓝牙设备 查看Bluetooth设备: hciconfig 启动一个Bluetooth设备,例如:hci0: hciconfig hci0 up 相关指令 查看特定的Bluetoo ...

  5. 全面详解C语言使用cJSON解析JSON字符[转载]

    cJSON对象的实现采用了树形结构,每个对象是树的一个节点,每个节点由cJSON这个结构体组成,对象中的元素也由cJSON这个结构体组成.同一层的对象和元素是双向链表结构,由next和prev指针链接 ...

  6. Android高版本Service在后台一分钟被杀死

    最近公司出现了一个Bug,Service在后台写log时候一分钟左右被杀死,或者运行一会就被杀死了,上网搜了一下原来是Android高版本为了保护电量,流量什么的,会在后台杀死这些Service,现在 ...

  7. SignalR 外部调用自定义Hub类的方法,Clients为null

    这是因为外部调用的类的对象和你连接的Hub类的对象,这两个对象 不!一!样! 解决方法 在自定义的Hub类中,注入IHubContext对象,然后在方法中调用IHubContext对象来向前端推送数据 ...

  8. linux rpm包下载

    下载地址 pkg.org 下载方法 搜索包名 找到需要的包 点击去,找到Download wget [Download url] 没有包管理器,恶心,真他妈恶心,我都不想使,还说啥稳定,环境都配不起来 ...

  9. SpringBoot - [02] 第一个SpringBoot程序

    jdk maven3.6.3 springboot最新版 idea 如果使用官网 Spring Initializr ,则需要jdk17.21.22,并且是Springboot3.x 可以在idea创 ...

  10. Ansible - [06] Playbook

    Playbook 概述 Ansible ad-hoc 可以通过命令行形式远程管理其他主机 适合执行一些临时性简单任务 Ansible playbook 中文名称叫 剧本 将经常需要执行的任务写入一个文 ...