TreeMap----源码分析

源码分析:

通过查看源码可以知道其实现以及继承。

public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable{}

在开头其定义了一些成员变量,在底层因为TreeMap是呈现红黑树结构,所以每个节点都具有树的特征。

//比较器,通过它来决定存储顺序
private final Comparator<? super K> comparator;
​
//根节点,
private transient Entry<K,V> root;
​
/*** 树中的节点个数*/
private transient int size = 0;
​
/*** 修改次数,防止并发修改*/
private transient int modCount = 0;
针对每个节点,TreeMap定义了内部类Entry进行封装:static final class Entry<K,V> implements Map.Entry<K,V> {//键K key;//值V value;//左节点Entry<K,V> left;//右节点Entry<K,V> right;//父节点Entry<K,V> parent;//颜色,初始化为黑色,但是后序会将其改为起始红色,因为红黑树规定,开始时每个节点是红色boolean color = BLACK;...
}

查看其构造其如下:

//空参构造,此时不设置比较器
public TreeMap() {comparator = null;
}
​
//有参构造一,此时需要传入比较器设置比较器
public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;
}
​
//可以传入已有的map,但是不设置比较器依旧置null
public TreeMap(Map<? extends K, ? extends V> m) {comparator = null;putAll(m);
}
​
//它允许用户快速地将一个已排序的映射转换为TreeMap,而不需要手动将每个元素插入到新的TreeMap中。这在需要保持元素顺序,并且已经有一个有序集合的情况下,可以大幅度提高效率。同时,它还保留了原始SortedMap的排序顺序和比较逻辑。
public TreeMap(SortedMap<K, ? extends V> m) {comparator = m.comparator();try {buildFromSorted(m.size(), m.entrySet().iterator(), null, null);} catch (java.io.IOException | ClassNotFoundException cannotHappen) {}
}
插入过程:

一般我们调用的是如下put方法,在底层会去调用一个三参构造方法

public V put(K key, V value) {return put(key, value, true);
}

该重载方法有三个,1:键,2:值,3是否覆盖:true是覆盖,false则是保留,这和hashmap刚好反过来,hashMap中定义的是onlyIfAbsent,表示是否保留和此处相反。

这里有个细节,就是TreeMap如果不初始化比较器,会将其置为null,那他的默认比较从何而来?那么此时会使用key进行比较,首先会假定其已经实现了Comparable接口,然后尝试调用其compareTo方法,但是你会发现TreeMap里面从头都没有重写compareTo方法,其实这个时候如果这个键是String,他会String类型中已经重写的compareTo方法,实现比较。

当然也存在使用一个没有实现Comparable接口的key进行比较,如果尝试执行treeMap.put(key, "Value for key");这一行,将会抛出ClassCastException,因为没有实现Comparable接口,TreeMap无法对MyObject的实例进行排序。此时只能传入一个自定义比较器。

private V put(K key, V value, boolean replaceOld) {//获取根节点Entry<K,V> t = root;//如果没有根节点,则此时插入的元素会变成根节点if (t == null) {addEntryToEmptyMap(key, value);return null;}//定义一个整型,因为比较器传回来的类型就是int,可以接收比较器的结果int cmp;//父节点Entry<K,V> parent;//获取比较器Comparator<? super K> cpr = comparator;//如果我们在使用构造方法传入了自定义比较器,此时会走这里的ifif (cpr != null) {do {parent = t;//使用自定义比较器cmp = cpr.compare(key, t.key);//根据返回值判断谁大谁小(顺序还是倒叙都靠比较器内部返回参数(o1-o2)顺序)//依照红黑树规则,小的在左边,大的在右边,相等则判断是否覆盖//当t不等于null就会一直循环if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else {V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);} //使用默认比较器,按 键(key) 进行比较else {Objects.requireNonNull(key);@SuppressWarnings("unchecked")//对我们的键进行强转类型,这样就可以使用其中的compareTo方法,此操作是假定key已经实现了Comparable方法Comparable<? super K> k = (Comparable<? super K>) key;do {//遍历的每个节点都赋值parent方便比较parent = t;//让我们的key与当前节点进行比较cmp = k.compareTo(t.key);//规则和上面提到的一致if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else {V oldValue = t.value;if (replaceOld || oldValue == null) {t.value = value;}return oldValue;}} while (t != null);}//调用方法插入我们的节点addEntry(key, value, parent, cmp < 0);return null;
}

没有节点时,会调用以下方法,

private void addEntryToEmptyMap(K key, V value) {//首先自己和自己比较(没有实际意义,追求一致性)compare(key, key); //让当前节点成为根节点,赋值给成员变量root = new Entry<>(key, value, null);//设置大小size = 1;//修改次数加一modCount++;
}

在put方法中,如果要插入元素时只是找到要插入元素的父节点parent,真正的插入操作在addEntry进行。

private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {//包装我们的插入节点Entry<K,V> e = new Entry<>(key, value, parent);//依据addToLeft进行判断,如果是左边则插左边。如果是右边则插入右边if (addToLeft)parent.left = e;elseparent.right = e;//判断红黑树是否需要调整fixAfterInsertion(e);//容量加一size++;//修改次数加一modCount++;
}

红黑树调整规则:

private void fixAfterInsertion(Entry<K,V> x) {//第一件事就是把颜色改成红色,因为红黑树规定默认是红色x.color = RED;//当插入节点不是根就会进入以下循环while (x != null && x != root && x.parent.color == RED) {//判断当前节点是不是爷爷节点的左子//目的是为了获取当前节点的叔叔节点if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {//进入if则表明当前节点它爹在左,则右节点就是它树,y就是叔叔节点Entry<K,V> y = rightOf(parentOf(parentOf(x)));//当叔叔节点为红色if (colorOf(y) == RED) {//将父节点设置为黑色setColor(parentOf(x), BLACK);//将叔叔也设置为黑色setColor(y, BLACK);//将爷爷设置为红色setColor(parentOf(parentOf(x)), RED);//将当前节点设置为爷爷节点,此时以爷爷为基准,等待下次判断x = parentOf(parentOf(x));} //当叔叔节点为黑色else {//先判断当前节点是父节点的右孩子还是左孩子if (x == rightOf(parentOf(x))) {//是右孩子//将父节点设置为当前节点x = parentOf(x);//以当前节点进行左旋rotateLeft(x);}//设置父节点为黑色setColor(parentOf(x), BLACK);//设置爷节点为红色setColor(parentOf(parentOf(x)), RED);//以爷爷节点为基准进行右旋rotateRight(parentOf(parentOf(x)));}} else {//进入if则表明当前节点它爹在右,则左节点就是它树,y就是叔叔节点Entry<K,V> y = leftOf(parentOf(parentOf(x)));//如果叔叔节点是红色if (colorOf(y) == RED) {//将父节点设置为黑色setColor(parentOf(x), BLACK);//将叔叔节点也设置为黑色setColor(y, BLACK);//将爷爷节点设置为红色setColor(parentOf(parentOf(x)), RED);//将爷爷节点设置为当前节点,等待下次判断x = parentOf(parentOf(x));} else {//当叔叔节点是黑色//判断当前节点是不是左节点if (x == leftOf(parentOf(x))) {//将父亲节点设置为当前节点x = parentOf(x);//进行右旋rotateRight(x);}//将父节点设置为黑色setColor(parentOf(x), BLACK);//将爷爷节点设置为红色setColor(parentOf(parentOf(x)), RED);//左旋rotateLeft(parentOf(parentOf(x)));}}}//将根节点设置为黑色root.color = BLACK;
}

具体规则可以参考如下:

 

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

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

相关文章

ZGC的流程图

GC标记过程 1、初始标记 扫描所有线程栈的根节点&#xff0c;然后再扫描根节点直接引用的对象并进行标记。这个阶段需要停顿所有的应用线程&#xff08;STW&#xff09;&#xff0c;但由于只扫描根对象直接引用的对象&#xff0c;所以停顿时间很短。停顿时间高度依赖根节点的数…

我的AI音乐梦:ChatGPT帮我做专辑

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;AI篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来ChatGPT帮我做音乐专辑 嘿&#xff0c;朋友们&#xff01; 想象一下&#xff0c;如果有个超级聪明的机器人能帮你写…

d3dcompiler_47.dll缺失怎么修复,一步步分析d3dcompiler_47.dll文件

d3dcompiler_47.dll缺失怎么修复&#xff1f;快速教大家解决出现d3dcompiler_47.dll问题的方法&#xff0c;一步步教大家快速有效的将丢失的d3dcompiler_47.dll如何修复。 一步步修复d3dcompiler_47.dll分析 1. 重新安装受影响的程序 如果是特定程序报告缺少d3dcompiler_47.d…

游戏缺失steam_api64.dll的多种解决方法,分享几种靠谱的方法

在使用电脑进行游戏的过程中&#xff0c;可能会出现“找不到steam_api.dll&#xff0c;无法继续执行代码”的提示&#xff0c;导致游戏无法正常运行。对于这样的情况&#xff0c;我们需要采取一定的措施进行修复。本文将为您提供相关的解决方案。 一、找不到steam_api.dll对电脑…

python-28-零基础自学python-json存数据、读数据,及程序合并

学习内容&#xff1a;《python编程&#xff1a;从入门到实践》第二版 知识点&#xff1a; import json引入、 try-except-else return def函数、打开文件、 练习内容&#xff1a; 练习10-11&#xff1a;喜欢的数 编写一个程序&#xff0c;提示用户输入喜欢的数&#xff…

【人工智能】knn算法

目录 一、对[1.0,1.1],[1.0,1.0],[0,0],[0,0.1],[0.1,0.1],[1.1,1.1]六个点用knn进行聚类&#xff0c;并显示。 1. 未调用KNN算法前&#xff0c;绿色为未知分类 2. 调用KNN算法 3. 运行结果 二、使用knn算法分类手写数字文件 1. 第一个temp的含义 2. 第二个temp的含义 …

【ARM】使用JasperGold和Cadence IFV科普

#工作记录# 原本希望使用CCI自带的验证脚本来验证修改过后的address map decoder&#xff0c;但是发现需要使用JasperGold或者Cadence家的IFV的工具&#xff0c;我们公司没有&#xff0c;只能搜搜资料做一下科普了解&#xff0c;希望以后能用到吧。这个虽然跟ARM没啥关系不过在…

基于神经网络的分类和预测

基于神经网络的分类和预测 一、基础知识&#xff08;一&#xff09;引言&#xff08;二&#xff09;神经网络的基本概念&#xff08;1&#xff09;神经网络&#xff08;2&#xff09;神经元&#xff08;3&#xff09;常用的激活函数&#xff08;非线性映射函数&#xff09;&…

【Linux网络】IP协议{初识/报头/分片/网段划分/子网掩码/私网公网IP/认识网络世界/路由表}

文章目录 1.入门了解2.认识报头3.认识网段4.路由跳转相关指令路由 该文诸多理解参考文章&#xff1a;好文&#xff01; 1.入门了解 用户需求&#xff1a;将我的数据可靠的跨网络从A主机送到B主机 传输层TCP&#xff1a;由各种方法&#xff08;流量控制/超时重传/滑动窗口/拥塞…

RAG 召回提升相关方案分享

最近大半年时间都在做RAG的工作&#xff0c;分享一点个人探索的方向。和提升的方案。文章中会分享是如何做的&#xff0c;以及对应的效果。 核心问题 如何提升RAG的效果&#xff1f; 如何提升召回的准确率。 写在前边&#xff1a;已验证的方案 方案 优化方向 效果 备注 3.1…

iPad锁屏密码忘记怎么办?有什么方法可以解锁?

当我们在日常使用iPad时&#xff0c;偶尔可能会遇到忘记锁屏密码的尴尬情况。这时&#xff0c;不必过于担心&#xff0c;因为有多种方法可以帮助您解锁iPad。接下来&#xff0c;小编将为您详细介绍这些解决方案。 一、使用iCloud的“查找我的iPhone”功能 如果你曾经启用了“查…

SSM学习6:Spring事务

简介 事务作用&#xff1a;在数据层保障一系列的数据库操作同成功同失败Spring事务作用&#xff1a;在数据层或业务层保障一系列的数据库操作同成功同失败 public interface PlatformTransactionManager{void commit(TransactionStatus status) throws TransactionStatus ;vo…

【网络文明】关注网络安全

在这个数字化时代&#xff0c;互联网已成为我们生活中不可或缺的一部分&#xff0c;它极大地便利了我们的学习、工作、娱乐乃至日常生活。然而&#xff0c;随着网络空间的日益扩大&#xff0c;网络安全问题也日益凸显&#xff0c;成为了一个不可忽视的全球性挑战。认识到网络安…

Python数据分析案例52——基于SSA-LSTM的风速预测(麻雀优化)

案例背景 又要开始更新时间序列水论文的系列的方法了&#xff0c;前面基于各种不同神经网络层&#xff0c;还有注意力机制做了一些缝合模型。 其实论文里面用的多的可能是优化算法和模态分解&#xff0c;这两个我还没出专门的例子&#xff0c;这几天正好出一个优化算法的例子来…

uboot学习:(四)顶层makefile分析

目录 版本号 MAKEFLAGS变量 命令输出 静默输出 设置编译结果输出目录 代码检查&#xff08;一般不需要使用&#xff0c;了解就行&#xff09; 模块编译&#xff08;一般不用uboot编译模块&#xff0c;了解就行&#xff09; 获取主机架构和系统 设置目标架构、交叉编译…

隔离驱动-视频课笔记

目录 1、需要隔离的原因 1.2、四种常用的隔离方案 2、脉冲变压器隔离 2.1、脉冲变压器的工作原理 2.2、泄放电阻对开关电路的影响 2.3、本课小结 3、光耦隔离驱动 3.1、光耦隔离驱动原理 3.2、光耦隔离驱动的电源进行分析 3.3、本课小结 4、自举升压驱动 4.1…

大数据开发中的元数据:从基础到高级应用的全面指南

在大数据开发中&#xff0c;元数据&#xff08;Metadata&#xff09;是指描述数据的数据。元数据可以提供有关数据结构、数据类型、数据约束和数据关系的重要信息。合理利用元数据可以显著提高数据建模和管理的效率。本文将详细介绍如何根据元数据建表&#xff0c;并提供一些代…

2024年公司电脑屏幕监控软件推荐|6款好用的屏幕监控软件盘点!

在当今的商业环境中&#xff0c;确保员工的工作效率和数据安全是每个企业管理者的重要任务。屏幕监控软件通过实时监控和记录员工的电脑活动&#xff0c;帮助企业有效地管理和优化工作流程。 1.固信软件 固信软件https://www.gooxion.com/ 主要特点&#xff1a; 实时屏幕监控…

养殖业饲料加工新选择,粉碎机械提升效率

在当今畜牧业快速发展的时代&#xff0c;饲料加工设备成为提升养殖效益的重要一环。其中&#xff0c;饲料加工粉碎机凭借其G效、便捷的特点&#xff0c;成为了养殖场的得力助手。 饲料加工粉碎机作为养殖业的重要设备之一&#xff0c;其主要功能是将各种原料如玉米、豆粕、麦…

Sentinel限流算法:滑动时间窗算法、漏桶算法、令牌桶算法。拦截器定义资源实现原理

文章目录 滑动时间窗算法基本知识源码算法分析 漏桶算法令牌桶算法拦截器处理web请求 滑动时间窗算法 基本知识 限流算法最简单的实现就是使用一个计数器法。比如对于A接口来说&#xff0c;我要求一分钟之内访问量不能超过100&#xff0c;那么我们就可以这样来实现&#xff1…