java源码-TreeMap

简介: 开篇 写TreeMap本身是一件让我感到无比怂逼的事情,因为红黑树的数据结构从大学到现在我就没弄明白过,估计在很长的一段时间里应该也弄不明白,不过我打算投入点时间去研究研究红黑树了。

开篇

 写TreeMap本身是一件让我感到无比怂逼的事情,因为红黑树的数据结构从大学到现在我就没弄明白过,估计在很长的一段时间里应该也弄不明白,不过我打算投入点时间去研究研究红黑树了。
 后来查看了下网上关于讲解TreeMap的文章,发现避开红黑树直接直接将它当做黑盒来讲的思路貌似也是可行的,所以我打算也从这个角度来写这个文章。


TreeMap的特性

  • TreeMap 是有序的,按照自然排序或者指定比较器排序,不允许 null 键,允许 null 值,是不同步。
  • TreeMap 是基于红黑树实现的最大的好处就是可以按照业务场景得到已排序的结果。


TreeMap类图

img_b6a77569da6219d407f5227dddcefcc2.png
TreeMap类图

说明:
 TreeMap实现了SortedMap接口并集成了AbstractMap,这些其实我并没有深究,只是想说明的是TreeMap是有序的。


TreeMap源码分析系列

TreeMap类成员变量

 TreeMap的核心数据结构当中,主要是root的数据节点,主要是用于标识红黑树的根节点。
 TreeMap的Entry就是用于保存Map的key/value数据结构,除了常见的key/value变量外,还有TreeMap特有的left指针,right指针,parent指针。

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
     // 比较器对象
    private final Comparator<? super K> comparator;
    // 根节点
    private transient Entry<K,V> root;
    // 集合大小
    private transient int size = 0;

    // 树结构被修改的次数
    private transient int modCount = 0;

    // 静态内部类用来表示节点类型
    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;
    }


TreeMap的构造函数

// 无参构造方法
public TreeMap() {
        // 默认比较机制,也就是key对象自带的比较机制
        comparator = null;
    }
// 自定义比较器的构造方法
public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

// 构造已知Map对象为TreeMap
public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null; // 默认比较机制
        putAll(m);
    }

// 构造已知的SortedMap对象为TreeMap
public TreeMap(SortedMap<K, ? extends V> m) {
        comparator = m.comparator(); // 使用已知对象的构造器
        try {
            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    }


TreeMap的put方法

 典型的使用红黑树的方法,通过比较器comparator的比较来维持全局有序,每次插入红黑树之后需要重新执行fixAfterInsertion来平衡红黑树。

public V put(K key, V value) {
        Entry<K,V> t = root;
        // 如果根节点为空也就是整个TreeMap为空的情况,走默认check检查key是否为null
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }

        int cmp;
        Entry<K,V> parent;
        Comparator<? super K> cpr = comparator;

        // 如果比较器对象不为空,也就是自定义了比较器
        if (cpr != null) {
            do { // 循环比较并确定元素应插入的位置(也就是找到该元素的父节点)
                parent = t; //parent代表新增节点应该保存的父节点
              
                // 调用比较器对象的compare()方法,该方法返回一个整数
                cmp = cpr.compare(key, t.key);
                if (cmp < 0) // 待插入元素的key"小于"当前位置元素的key,则查询左子树
                    t = t.left;
                else if (cmp > 0) // 待插入元素的key"大于"当前位置元素的key,则查询右子树
                    t = t.right;
                else
                    return t.setValue(value);  // "相等"则替换其value。
            } while (t != null);
        }
      
        // 如果比较器对象为空,使用默认的比较机制
        else {
            if (key == null) //不支持key为null的情况
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key; // 取出比较器对象
            do {// 同样是循环比较并确定元素应插入的位置(也就是找到该元素的父节点)
                parent = t;
                cmp = k.compareTo(t.key); // 调用比较方法并返回一个整数
                if (cmp < 0) // 待插入元素的key"小于"当前位置元素的key,则查询左子树
                    t = t.left;
                else if (cmp > 0) // 待插入元素的key"大于"当前位置元素的key,则查询右子树
                    t = t.right;
                else
                    return t.setValue(value); // "相等"则替换其value
            } while (t != null);
        }
        
        // 根据key找到父节点后新建一个节点
        Entry<K,V> e = new Entry<>(key, value, parent);
        // 根据比较的结果来确定放在左子树还是右子树
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        // 重新维持红黑树的平衡
        fixAfterInsertion(e);

        // 集合大小+1
        size++;
        // 集合结构被修改次数+1
        modCount++;
        return null;
    }


TreeMap的get相关方法

 TreeMap的get相关方法说白了就是在TreeMap是二叉平衡树的基础上进行比较查询的,比当前key小就查找左子树,比当前key大就查找右子树,如果相等就说明找到了。

public V get(Object key) {
        Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
    }


final Entry<K,V> getEntry(Object key) {
        // 如果有比较器,那么就用比较器
        if (comparator != null)
            return getEntryUsingComparator(key);

        // 如果没有比较器,那么就用key自带的比较器进行比较
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        Entry<K,V> p = root;

        // 由于TreeMap本身就是一个有序的二叉树,所以直接通过二飞查找就可以了
        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;
    }

// 同样通过二分查找就可以了
final Entry<K,V> getEntryUsingComparator(Object key) {
        @SuppressWarnings("unchecked")
            K k = (K) key;
        
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            Entry<K,V> p = root;
            while (p != null) {
                int cmp = cpr.compare(k, p.key);
                if (cmp < 0)
                    p = p.left;
                else if (cmp > 0)
                    p = p.right;
                else
                    return p;
            }
        }
        return null;
    }

 firstKey()方法就是采用二叉平衡树的特性获取最左子树的值。

public K firstKey() {
        return key(getFirstEntry());
    }

final Entry<K,V> getFirstEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
    }

 lastKey()方法就是采用二叉平衡树的特性获取最右子树的值。

public K lastKey() {
        return key(getLastEntry());
    }


final Entry<K,V> getLastEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.right != null)
                p = p.right;
        return p;
    }

 pollFirstEntry和pollLastEntry说白了也是TreeMap是二叉平衡树的基础上直接查找最左子树或查找最右子树返回结果。

public Map.Entry<K,V> pollFirstEntry() {
        Entry<K,V> p = getFirstEntry();
        Map.Entry<K,V> result = exportEntry(p);
        if (p != null)
            deleteEntry(p);
        return result;
    }

final Entry<K,V> getFirstEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
    }

public Map.Entry<K,V> pollLastEntry() {
        Entry<K,V> p = getLastEntry();
        Map.Entry<K,V> result = exportEntry(p);
        if (p != null)
            deleteEntry(p);
        return result;
    }

final Entry<K,V> getLastEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.right != null)
                p = p.right;
        return p;
    }


TreeMap的Iterator相关方法

 TreeMap的iterator方法其实就是按照平衡二叉树的左中右的顺序进行迭代遍历的,具体来说就是先遍历左子树,然后遍历父节点,最后遍历右子树。
 遍历过程中successor函数比较关键,相关注释已写明

public Set<Map.Entry<K,V>> entrySet() {
        EntrySet es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }


class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return new EntryIterator(getFirstEntry());
        }
    }


final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
        EntryIterator(Entry<K,V> first) {
            super(first);
        }
        public Map.Entry<K,V> next() {
            return nextEntry();
        }
    }


abstract class PrivateEntryIterator<T> implements Iterator<T> {
        Entry<K,V> next;
        Entry<K,V> lastReturned;
        int expectedModCount;

        PrivateEntryIterator(Entry<K,V> first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first;
        }

        public final boolean hasNext() {
            return next != null;
        }

        // 遍历的核心在于successor()方法,遍历顺序是二叉树的中序遍历法
        final Entry<K,V> nextEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = successor(e);
            lastReturned = e;
            return e;
        }

        final Entry<K,V> prevEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = predecessor(e);
            lastReturned = e;
            return e;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // deleted entries are replaced by their successors
            if (lastReturned.left != null && lastReturned.right != null)
                next = lastReturned;
            deleteEntry(lastReturned);
            expectedModCount = modCount;
            lastReturned = null;
        }
    }

// successor的逻辑其实就是中序遍历的逻辑
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
        if (t == null)
            return null;
        // 如果右子树不为空,那么就找到右子树的最左子树
        else if (t.right != null) {
            Entry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else {
      // 如果右子树为空,那么就找父节点,因为中序遍历是左中右
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            while (p != null && ch == p.right) { 
            // 如果该节点刚好是父节点的右子节点,
            // 那么就证明父节点的右子树遍历完毕且本身也遍历完毕,
            // 那么就继续往上找父节点
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }


参考文献

教你初步了解红黑树

目录
相关文章
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
1003 7
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
578 5
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
存储 Java
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
791 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
894 70
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
554 6
家政系统源码,java版本
|
Java
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
因为本文说的是ReentrantLock源码,因此会默认,大家对AQS有基本的了解(比如同步队列、条件队列大概> 长啥样?)。 不懂AQS的小朋友们,你们好呀!也欢迎先看看这篇
374 13
【源码】【Java并发】【ReentrantLock】适合中学者体质的ReentrantLock源码阅读
|
Java
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
前言 主播觉得,AQS的原理,就是通过这2个队列的协助,实现核心功能,同步队列(CLH队列)和条件队列(Condition队列)。 同步队列(CLH队列) 作用:管理需要获...
387 18
【源码】【Java并发】【AQS】从ReentrantLock、Semaphore、CutDownLunch、CyclicBarrier看AQS源码
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
1167 23