TreeMap详解

TreeMap详解一 概念及概述 TreeMap 是一个有序的 key value 集合 非同步 基于红黑树 Red Blacktree 实现 每个 key value 作为红黑树的一个节点 TreeMap 存储时会进行排序的 会根据 key 来对 key value 键值对进行排序 其中排序方式也是分为两种 一种是默认排序 按 key 的升序 一种是定制排序 具体取决于使用的构造方法 二 插入插入操作比较复杂一

一、概念及概述

TreeMap 是一个有序的key-value集合,非同步基于红黑树(Red-Black tree)实现,每个key-value作为红黑树的一个节点。

TreeMap存储时会进行排序的,会根据key来对key-value键值对进行排序,其中排序方式也是分为两种,一种是默认排序(按key的升序),一种是定制排序,具体取决于使用的构造方法。

 

二、插入

插入操作比较复杂一些,当往 TreeMap 中放入新的键值对后,可能会破坏红黑树的性质,在此只做简单说明

源码分析

public V put(K key, V value) { Entry 
  
    t = root; // 1.如果根节点为 null,将新节点设为根节点 if (t == null) { compare(key, key); root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry 
   
     parent; // split comparator and comparable paths Comparator 
     cpr = comparator; if (cpr != null) { // 2.为 key 在红黑树找到合适的位置 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 { // 与上面代码逻辑类似,省略 } Entry 
    
      e = new Entry<>(key, value, parent); // 3.将新节点链入红黑树中 if (cmp < 0) parent.left = e; else parent.right = e; // 4.插入新节点可能会破坏红黑树性质,这里修正一下 fixAfterInsertion(e); size++; modCount++; return null; } 
     
    
  

 

 

三、查找

TreeMap基于红黑树实现,而红黑树是一种自平衡二叉查找树,所以 TreeMap 的查找操作流程和二叉查找树一致。二叉树的查找流程是这样的,先将目标值和根节点的值进行比较,如果目标值小于根节点的值,则再和根节点的左孩子进行比较。如果目标值大于根节点的值,则继续和根节点的右孩子比较。在查找过程中,如果目标值和二叉树中的某个节点值相等,则返回 true,否则返回 false。TreeMap 查找和此类似,只不过在 TreeMap 中,节点(Entry)存储的是键值对

。在查找过程中,比较的是键的大小,返回的是值,如果没找到,则返回null。TreeMap 中的查找方法是get,具体实现在getEntry方法中。

相关源码如下:

public V get(Object key) { Entry 
  
    p = getEntry(key); return (p==null ? null : p.value); } final Entry 
   
     getEntry(Object key) { // Offload comparator-based version for sake of performance if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable 
     k = (Comparable 
    ) key; Entry 
    
      p = root; // 查找操作的核心逻辑就在这个 while 循环里 while (p != null) { int cmp = k.compareTo(p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } return null; } 
     
    
  

 

四、删除

删除操作是红黑树最复杂的部分,原因是该操作可能会破坏红黑树性质5(从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点),修复性质5要比修复其他性质(性质2和4需修复,性质1和3不用修复)复杂的多。

红黑树的五个特性:

删除的相关源码如下:

public V remove(Object key) { Entry 
  
    p = getEntry(key); if (p == null) return null; V oldValue = p.value; deleteEntry(p); return oldValue; } private void deleteEntry(Entry 
   
     p) { modCount++; size--; /* * 1. 如果 p 有两个孩子节点,则找到后继节点, * 并把后继节点的值复制到节点 P 中,并让 p 指向其后继节点 */ if (p.left != null && p.right != null) { Entry 
    
      s = successor(p); p.key = s.key; p.value = s.value; p = s; } // p has 2 children // Start fixup at replacement node, if it exists. Entry 
     
       replacement = (p.left != null ? p.left : p.right); if (replacement != null) { /* * 2. 将 replacement parent 引用指向新的父节点, * 同时让新的父节点指向 replacement。 */ replacement.parent = p.parent; if (p.parent == null) root = replacement; else if (p == p.parent.left) p.parent.left = replacement; else p.parent.right = replacement; // Null out links so they are OK to use by fixAfterDeletion. p.left = p.right = p.parent = null; // 3. 如果删除的节点 p 是黑色节点,则需要进行调整 if (p.color == BLACK) fixAfterDeletion(replacement); } else if (p.parent == null) { // 删除的是根节点,且树中当前只有一个节点 root = null; } else { // 删除的节点没有孩子节点 // p 是黑色,则需要进行调整 if (p.color == BLACK) fixAfterDeletion(p); // 将 P 从树中移除 if (p.parent != null) { if (p == p.parent.left) p.parent.left = null; else if (p == p.parent.right) p.parent.right = null; p.parent = null; } } } 
      
     
    
  

上面对 fixAfterDeletion 部分代码逻辑就行了分析,通过配图的形式解析了每段代码逻辑所处理的情况。

 

五、遍历

 遍历操作也是大家使用频率较高的一个操作,对于TreeMap,使用方式一般如下:

for(Object key : map.keySet()) { // do something }

或者

for(Map.Entry entry : map.entrySet()) { // do something }
Set keys = map.keySet(); Iterator ite = keys.iterator(); while (ite.hasNext()) { Object key = ite.next(); // do something }

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220658.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午7:57
下一篇 2026年3月17日 下午7:57


相关推荐

  • Django(26)HttpResponse对象和JsonResponse对象「建议收藏」

    Django(26)HttpResponse对象和JsonResponse对象「建议收藏」HttpResponse对象Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个HttpRequest对象传给视图函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响

    2022年7月30日
    7
  • 微软Agent 365助力企业在AI智能体造成问题前发现风险

    微软Agent 365助力企业在AI智能体造成问题前发现风险

    2026年3月13日
    1
  • c语言中fprintf_c语言输出函数printf

    c语言中fprintf_c语言输出函数printf目录一.fprintf函数简介二.fprintf函数使用三.猜你喜欢零基础C/C++学习路线推荐:C/C++学习目录>>C语言基础入门一.fprintf函数简介fprintf是C/C++中的一个格式化库函数,位于头文件中,其作用是格式化输出到一个流文件中;函数原型为/**描述:fputs函数是向指定的文件写入一个字符串**参数:*[in]stream:文件指针句柄;*[in]format:格式化字符串,

    2022年10月10日
    4
  • ioctl之FIONREAD

    ioctl之FIONREAD在学习ioctl时常常跟read,write混淆。其实ioctl是用来设置硬件控制寄存器,或者读取硬件状态寄存器的数值之类的。而read,write是把数据丢入缓冲区,硬件的驱动从缓冲区读取数据一个个发送或者把接收的数据送入缓冲区。 ioctl(keyFd,FIONREAD,&b)得到缓冲区里有多少字节要被读取,然后将字节数放入b里面。接下来就可以

    2022年7月23日
    20
  • 前端程序员常用Html还是JSP格式

    前端程序员常用Html还是JSP格式Html 和 JSP 的简介 HTML HypertextMar 文本标记语言 它是静态页面 和 JavaScript 一样解释性语言 为什么说是解释性语言呢 因为 只要你有一个浏览器那么它就可以正常显示出来 而不需要指定的编译工具 只需在 TXT 文档中写上 HTML 标记就 OK JSP JavaServerPa 是 Java 服务端的页面 所以它是动态的 它是需要经过 JD

    2026年3月16日
    2
  • acwing-2189. 有源汇上下界最大流

    acwing-2189. 有源汇上下界最大流给定一个包含 n 个点 m 条边的有向图,每条边都有一个流量下界和流量上界。给定源点 S 和汇点 T,求源点到汇点的最大流。输入格式第一行包含四个整数 n,m,S,T。接下来 m 行,每行包含四个整数 a,b,c,d 表示点 a 和 b 之间存在一条有向边,该边的流量下界为 c,流量上界为 d。点编号从 1 到 n。输出格式输出一个整数表示最大流。如果无解,则输出 No Solution。数据范围1≤n≤202,1≤m≤9999,1≤a,b≤n,0≤c≤d≤105输入样例:10

    2022年8月9日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号