java源码阅读 - TreeSet

news/2024/11/23 23:57:43/

往期文章

  • 用最简单的话讲最明白的红黑树
  • java源码阅读 - HashMap
  • 数据结构 - 堆与堆排序

文章目录

  • 往期文章
  • 一、介绍
  • 二、类的声明
  • 三、成员变量
  • 四、构造函数
  • 五、常用方法
    • 1. NavigableSet接口的实现
    • 2. SortedSet接口的实现
  • 六、总结

一、介绍

在上期文章中,我们从源码层面详细分析了java集合框架中Set一族的实现HashSet,它的内部维护了一个HashMap对象作为内部存储,也就是说HashSet的底层结构为哈希表,今天我们介绍Set的另一个实现——TreeSet,对标HashSet与HashMap的关系,我们猜想TreeSet可能会维护一个TreeMap作为内部存储,事实也确实如此,因此,TreeSet的特性均继承于TreeMap,如元素有序等。在学习TreeSet源码之前,必须对TreeMap的源码了如指掌。由于TreeMap底层实现为较复杂的红黑树,对TreeMap源码不了解的同学请一定要参考前面的文章TreeMap源码。下面我们先看一下TreeSet的UML图。

在这里插入图片描述

二、类的声明

public class TreeSet<E> extends AbstractSet<E>implements NavigableSet<E>, Cloneable, java.io.Serializable

从类的声明和上面的UML类图中可以了解到:

  • 继承AbstractSet抽象类,对其进行了扩展。
  • 实现了NavigableSet接口,表示它是一个提供导航功能的Set集合,满足Set集合的定义
  • 实现了Cloneable接口,提供了对象克隆方法,但请注意,是浅克隆
  • 实现了Serializable接口,支持序列化

三、成员变量

// HashSet的底层使用NavigableMap对象作为存储结构,
// 目前我们常用的NavigableMap实现为TreeMap
// 而TreeMap已经实现了红黑树,因此TreeSet无需再次对红黑树进行实现,直接通过TreeMap对红黑树进行数据的存取即可。
private transient NavigableMap<E,Object> m;// 虽然TreeSet使用TreeMap来操作红黑树,
// 但是TreeMap存储的是<key,value>键值对,
// 因此TreeSet在保存数据时,key为实际保存的数据,使用一个固定的对象PRESENT作为假的value值
private static final Object PRESENT = new Object();

四、构造函数

TreeSet提供了四个构造函数,由于TreeSet的底层为TreeMap,因此在TreeSet的构造方法中,都是先实例化一个TreeMap对象。

在TreeSet的众多构造函数中,有一个比较特殊的构造函数

TreeSet(NavigableMap<E,Object> m) {this.m = m;
}

该构造函数的访问修饰符为默认,因此只能被类内部和同包内的子类调用。通过接收一个NavigableMap对象,并将其作为内部的NavigableMap对象维护,比如TreeMap。

  • 无参构造

    通过TreeMap的无参构造,实例化一个TreeMap对象,作为TreeSet的内部成员变量。

    public TreeSet() {this(new TreeMap<E,Object>());
    }
    
  • 指定比较器Comparator

    其实还是调用TreeMap的构造方法public TreeMap(Comparator<? super K> comparator)来实例化一个TreeMap对象,作为TreeSet的内部成员变量。

    public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));
    }
    
  • 通过传入一个集合构造

    先通过无参构造,实例化TreeSet对象,然后将集合中的元素通过addAll()方法批量保存到TreeSet对象中。

    其中addAll()方法的实现与TreeMap中putAll()方法的实现几乎完全相同,故不再赘述,可查看前面的文章TreeMap

    public TreeSet(Collection<? extends E> c) {this();addAll(c);
    }public  boolean addAll(Collection<? extends E> c) {// Use linear-time version if applicableif (m.size()==0 && c.size() > 0 &&c instanceof SortedSet &&m instanceof TreeMap) {SortedSet<? extends E> set = (SortedSet<? extends E>) c;TreeMap<E,Object> map = (TreeMap<E, Object>) m;Comparator<?> cc = set.comparator();Comparator<? super E> mc = map.comparator();if (cc==mc || (cc != null && cc.equals(mc))) {map.addAllForTreeSet(set, PRESENT);return true;}}return super.addAll(c);
    }
    
  • 通过传入一个有序集合构造

    在有序集合SortedSet中,提供给我们一个方法comparator()来获取有序集合中的比较器,再根据这个比较器来实例化一个TreeSet对象。如果有序集合中不存在比较器Comparator,则从TreeMap中我们也知道,键值对中的key必须实现Compareable接口中的compareTo()方法。

    其中addAll()方法的实现与TreeMap中putAll()方法的实现几乎完全相同,故不再赘述,可查看前面的文章TreeMap。

    public TreeSet(SortedSet<E> s) {this(s.comparator());addAll(s);
    }
    

五、常用方法

由于TreeSet实现NavigableSet接口,NavigableSet接口又继承于SortedSet接口。而TreeMap实现NavigableMap接口,NavigableMap接口又继承于SortedMap接口,如下图所示:

在这里插入图片描述

二者相似度极高,因此我们可以断定

  • SortedSet接口的方法都是通过调用TreeMap中实现SortedMap接口方法而实现的。

  • NavigableSet接口的方法都是通过调用TreeMap中实现NavigableMap接口方法而实现的。

下面我们通过源码进行分析:

1. NavigableSet接口的实现

  • lower()

    获取比指定元素小的元素中最大的一个元素。调用内部TreeMap对象实现NavigableMap接口的lowerKey()方法,

    public E lower(E e) {return m.lowerKey(e);
    }
    
  • floor()

    获取小于等于指定元素的元素中最大的一个元素。调用内部TreeMap对象实现NavigableMap接口的floorKey()方法,

    public E floor(E e) {return m.floorKey(e);
    }
    
  • ceiling()

    获取大于等于指定元素的元素中最小的一个元素。调用内部TreeMap对象实现NavigableMap接口的ceilingKey()方法,

    public E ceiling(E e) {return m.ceilingKey(e);
    }
    
  • higher()

    获取比指定元素大的元素中最小的一个元素。调用内部TreeMap对象实现NavigableMap接口的higherKey()方法,

    public E higher(E e) {return m.higherKey(e);
    }
    
  • pollFirst()

    获取set集合中第一个元素,并将其删除。调用内部TreeMap对象实现NavigableMap接口的pollFirstEntry()方法,获取首个键值对节点并删除后,返回该键值对节点中的key。

    public E pollFirst() {Map.Entry<E,?> e = m.pollFirstEntry();return (e == null) ? null : e.getKey();
    }
    
  • pollLast()

    获取set集合中最后一个元素,并将其删除。调用内部TreeMap对象实现NavigableMap接口的pollLastEntry()方法,获取最后一个键值对节点并删除后,返回该键值对节点中的key。

    public E pollLast() {Map.Entry<E,?> e = m.pollLastEntry();return (e == null) ? null : e.getKey();
    }
    

2. SortedSet接口的实现

  • comparator()

    获取当前实例中的比较器

    public Comparator<? super E> comparator() {return m.comparator();
    }
    
  • first()

    获取set集合中第一个元素。调用内部TreeMap对象实现SortedMap接口的firstKey()方法,获取首个键值对节点中的key。

    public E first() {return m.firstKey();
    }
    
  • last()

    获取set集合中最后一个元素。调用内部TreeMap对象实现SortedMap接口的lastKey()方法,获取最后一个键值对节点中的key。

    public E last() {return m.lastKey();
    }
    

六、总结

  • 元素有序,基于比较器或Compareable接口的compareTo()方法实现元素的排序
  • 线程不安全
  • TreeSet的底层实现为TreeMap,TreeMap的底层实现为红黑树,因此TreeSet的底层实现为红黑树,TreeSet通过调用TreeMap的方法对红黑树进行操作。


纸上得来终觉浅,绝知此事要躬行。

————————————————我是万万岁,我们下期再见————————————————


http://www.ppmy.cn/news/37927.html

相关文章

【动态规划】LeetCode 312. 戳气球 --区间DP问题

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…

【动力节点】王鹤SpringBoot笔记——第五章 说说Web服务

目录 第五章 说说Web服务 5.1 高效构建Web应用 5.1.1 html页面视图 5.1.2 JSON视图 5.1.3 给项目加favicon 5.2 Spring MVC 5.2.1 控制器Controller 5.2.1.1 匹配请求路径到控制器方法 5.2.1.2 RequestMapping 5.2.1.3 控制器方法参数类型与可用返回值类型 5…

迅为3A5000_7A2000工控主板,龙芯自主指令集架构全国产工业级板卡性能

迅为iTOP-3A5000开发板核心板底板方式&#xff0c;底板资料开源&#xff0c;提供底板的原理图和PCB工程文件&#xff0c;可以根据需求定制属于自己的开发板。 核心板也支持工业级核心板定制开发。 根据二进制翻译功能使用&#xff0c;可流畅运行WIN和Android系统APP。 支持Loo…

Spring Boot集成Redis实现Key事件监听 | Spring Cloud 33

一、前言 在前面我们通过以下章节对Redis使用有了基础的了解&#xff1a; Spring Boot实现Redis同数据源动态切换DB | Spring Cloud 31 Spring Boot实现Redis多数据源动态切换 | Spring Cloud 32 此章节基于spring-boot-starter-data-redis模块&#xff0c;实现对Key事件监…

LeetCode222. 完全二叉树的节点个数(二分查找+二进制表示路径法)

一、题目描述 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的…

【vim进阶】vim编辑器的分屏操作(分屏显示文件,关闭分屏,分屏间光标的移动,移动分屏)

一、分屏显示文件 VIM 可以实现分屏操作&#xff0c;一个屏幕被多个文件给分占&#xff0c;有左右和上下两种分屏的方式。 方法一&#xff1a;启动分屏 左右分屏如下操作&#xff1a; vim -On file1 file2 ... filenn是数字&#xff0c;表示分屏的数量,n要大于等于文件个数…

设计模式---装饰模式

目录 介绍 实现 优缺点 装饰模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于结构型模式&#xff0c;它是作为现有类的一个包装。这种模式创建了一个装饰类&#xff0c;用来包装原有…

【新2023Q2押题JAVA】华为OD机试 - 找字符

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:找字符 题目 给定两个字符串…