当前位置: 首页 > news >正文

Java 黑马程序员学习笔记(进阶篇14)

1. 红黑树

(1) 红黑规则

红黑树通过以下规则保证 “自平衡”(本质是控制树的整体形态,避免极端不平衡):

  • 颜色二选一:每个节点的颜色只能是红色或黑色。
  • 根黑原则:根节点必须是黑色。
  • 叶黑原则:叶节点(用 NIL 表示,无子女 / 父节点的 “空节点”)是黑色。
  • 红节点的子节点必黑:若一个节点是红色,它的子节点必须是黑色(禁止 “红 - 红” 连续)。
  • 黑高一致:对任意节点,从该节点到其所有后代叶节点的路径上,包含的黑色节点数目相同(“黑高” 相同)。

(2) 默认颜色:新节点默认红色

红黑树添加新节点时,默认将节点颜色设为红色。这样设计的优势是:红色节点插入后更不容易直接违反红黑规则(尤其是 “不能有两个红色节点连续相连” 的规则),从而减少初始调整的概率,提升插入效率。

(3) 添加节点的分支处理规则

根据 “新节点是否为根”“父节点颜色”“叔叔节点颜色”“节点位置(父的左 / 右孩子)”,分支处理以满足红黑规则:

场景分类具体条件处理方式
新节点是根节点——直接将新节点颜色改为黑色(满足 “根节点必须是黑色” 的规则)。
新节点是非根,且父节点为黑色——无需任何操作(不违反红黑规则,5 条核心规则仍成立)。
新节点是非根,且父节点为红色需进一步判断 “叔叔节点颜色” 和 “当前节点相对父的位置”,因为违反了 “不能有两个红色节点连续相连” 的规则。1. 叔叔节点为红色:- 父、叔叔设为黑色;- 祖父设为红色;- 若祖父是根,再将祖父改回黑色;- 若祖父非根,将祖父作为新的 “当前节点”,继续上层判断。2. 叔叔节点为黑色,且当前节点是父的右孩子:- 将 “父节点” 作为新的 “当前节点”,执行左旋,转化为后续可处理的形态。3. 叔叔节点为黑色,且当前节点是父的左孩子:- 父设为黑色,祖父设为红色;- 以祖父节点为支点执行右旋,调整结构使规则满足。

2. Set 系列集合

(1) 核心特点:

① 整体特性Set 是 Collection 接口的子接口,具备 无序(存储顺序与添加顺序无关)、不重复(自动去重)、无索引 的特点;方法与 Collection 的 API 基本一致(如 addremovecontains 等)。

② 实现类差异

  • HashSet无序、不重复、无索引,是最常用的 Set 实现类。
  • LinkedHashSet有序(保持元素 “插入顺序”)、不重复、无索引,底层通过 “链表 + 哈希表” 维护顺序。
  • TreeSet可排序(支持自然排序 / 自定义比较器排序)、不重复、无索引,底层基于红黑树实现排序。

3. HashSet 详解

(1) HashSet 的底层原理

HashSet 底层依赖哈希表(JDK8 后为 “数组 + 链表 + 红黑树” 的组合结构),核心机制与性能优化和红黑树密切相关:

  • 数组:称为「哈希表」或「桶(Bucket)」,存储元素的引用(或链表头节点)。
  • 链表:当多个元素的「哈希值」对应同一个数组下标时,用链表存储这些元素(解决「哈希冲突」)。
  • 红黑树:当链表长度超过 8,且数组长度 ≥ 64 时,链表会自动转为红黑树(将查询效率从 O (n) 优化为 O (logn))。
(2) HashSet 的去重原理

HashSet 之所以能「去重」,依赖 hashCode () 方法和 equals () 方法的协同工作,流程如下:

① 当向 HashSet 中添加元素 e 时,先调用 e.hashCode() 计算「哈希值」,根据哈希值确定元素在数组中的「目标下标」。

② 检查目标下标位置:

  • 若该位置为空:直接将元素存入(无重复)。
  • 若该位置不为空:遍历该位置的链表 / 红黑树,逐个比较已有元素与 e
    • 先比较「哈希值」:若哈希值不同 → 不是重复元素,直接添加到链表 / 红黑树。
    • 若哈希值相同 → 再调用 equals() 比较内容:若 equals() 返回 true → 是重复元素,拒绝添加;若返回 false → 不是重复元素,添加到链表 / 红黑树。

③ 关键结论:存储自定义对象时,必须重写 hashCode () 和 equals (),否则无法实现去重(默认用 Object 类的方法,比较的是地址值)。

(3) 案例 2:存储自定义对象(需重写 hashCode () 和 equals ())

需求:存储「学生对象」,要求「学号相同则视为重复元素」。

import java.util.HashSet;// 学生类
class Student {private String id;   // 学号(唯一标识)private String name;// 构造方法public Student(String id, String name) {this.id = id;this.name = name;}// 重写 toString():便于打印@Overridepublic String toString() {return "Student{id='" + id + "', name='" + name + "'}";}// 核心:重写 hashCode() 和 equals()(根据学号去重)@Overridepublic boolean equals(Object o) {if (this == o) return true; // 地址相同 → 同一对象if (o == null || getClass() != o.getClass()) return false; // 类型不同 → 不相等Student student = (Student) o;return id.equals(student.id); // 学号相同 → 相等}@Overridepublic int hashCode() {return id.hashCode(); // 哈希值由学号决定(保证学号相同的对象哈希值相同)}
}// 测试类
public class HashSetDemo2 {public static void main(String[] args) {HashSet<Student> studentSet = new HashSet<>();// 添加学生(学号相同的视为重复)studentSet.add(new Student("2024001", "张三"));studentSet.add(new Student("2024002", "李四"));studentSet.add(new Student("2024001", "张三")); // 学号相同,重复元素,添加失败// 遍历for (Student s : studentSet) {System.out.println(s); // 输出:// Student{id='2024001', name='张三'}// Student{id='2024002', name='李四'}}}
}

4. LinkedHashSet 详解

(1) 核心定义

LinkedHashSet 是 HashSet 的子类,底层基于「哈希表 + 双向链表」实现,核心特点是「保证元素的插入顺序」(解决 HashSet 的无序问题)。

(2) 核心特点
  • 继承 HashSet 的所有特点:无重复、线程不安全、允许存 1 个 null。
  • 额外特点:有序性:元素的遍历顺序 ≠ 存储顺序,而是 与插入顺序完全一致(底层双向链表维护顺序)。
(3) 底层原理

LinkedHashSet 底层依赖 LinkedHashMap 实现(构造方法创建 LinkedHashMap),而 LinkedHashMap 的底层是「哈希表(数组 + 链表 / 红黑树) + 双向链表」:

  • 哈希表:作用与 HashSet 一致,负责快速存储和去重(依赖 hashCode () 和 equals ())。
  • 双向链表:额外维护元素的「插入顺序」—— 每个元素除了存储哈希表的关联信息,还会记录「前驱元素」和「后继元素」的引用,遍历时分直接按双向链表顺序读取,保证插入顺序。

5. TreeSet

(1) 概述

TreeSet 是 Java 集合框架中 Set 接口 的一个实现类,它的特点是:

  • 元素唯一(无重复)
  • 自动排序(默认升序,或按指定规则排序)
  • 底层基于 TreeMap(红黑树结构)
(2) TreeSet 第一种排序方式

① 自然排序(Comparable)

  • 元素类必须实现 java.lang.Comparable 接口
  • 并重写 compareTo() 方法,定义排序规则
  • 这是 TreeSet 的默认排序方式

② 示例:

package demo3;public class Student implements Comparable<Student> {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Student o) {// 先按年龄升序排序int result = this.age - o.age;// 如果年龄相同,再按姓名升序排序if (result == 0) {result = this.name.compareTo(o.name);}return result;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
(3) TreeSet 第一种排序方式

① 比较器排序(Comparator)

  • 创建 TreeSet 时,传入一个 Comparator 接口的实现类对象
  • 可以不用修改元素类代码,灵活定义排序规则

② 示例分析:

    public int compareTo(Student o) {int sum1 = this.getChinese() + this.getMath() + this.getEnglish();   //不太理解,// 为什么要用this,为什么要用getChinese(),而不是直接chinese,不是同一个类里面吗int sum2 = o.getChinese() + o.getMath() + o.getEnglish();   //不太理解

Q1:为什么用 getChinese() 而不是直接 chinese

A1:访问权限问题:

  • 如果 chinese 是 private,那么只能在当前类的当前实例中直接访问
  • 对于另一个对象 o(即使是同一个类),你不能直接访问 o.chinese
  • 所以必须用 o.getChinese() 这种公共方法去获取值。

Q2:为什么要用 this

A2:this 代表当前对象(调用 compareTo 方法的那个对象)。

在 compareTo 里,你需要比较两个对象

  • this → 自己(调用者)
  • o → 另一个对象(参数)
http://www.dtcms.com/a/415574.html

相关文章:

  • 网站开发的理解制作网站软件网站
  • 长沙网页网站制作网站建设常用的工具
  • 上海装修网站建设深圳安全教育平台
  • 房子装修报价清单表湖北seo网站多少钱
  • 列举网站开发常用的工具免费软件有哪些
  • jsp网站开发环境配置直播网站开发需要多少钱
  • Ingress:轻松拿捏集群流量管理
  • 网站正在建设中...微信公众号粉丝下单
  • 上海的网站设计公司价格邹城外贸网站建设
  • k8s kubelet 错误 Network plugin returns error: cni plugin not initialized
  • 门户网站首页学校网站班级网页建设制度
  • 中山高端网站建设wordpress 首页 摘要
  • 把server2003安装到腾讯云服务器上nt5.2.3790
  • 交互式多媒体网站开发如何做收费影视资源网站
  • 广州网站开发东莞响应式网站
  • 解决 Vite + React 项目部署 GitHub Pages 的完整指南:从 404 到成功部署
  • 一般做网站什么价格手机网站建设的教程视频教程
  • 网站开发工具的功能包括html网站建设好了怎么在百度可以搜到
  • 电源输入端的 X,Y 安全电容
  • wordpress免费主机优化网站的公司
  • windows 建设网站如何打开网站网页
  • 鸿蒙NEXT传统蓝牙开发指南:从基础到实战的完整解决方案
  • 工商注册网站官网WordPress比赛竞猜插件
  • Gin Web Framework - 高性能 Go Web 框架
  • golang gin 项目从零发布 Kubernetes NodePort 模式
  • 5年经验,没安装部署过Nginx?
  • Java面试-并发面试(二)
  • 纺织网站制作123纺织网科技小制作小发明
  • HashMap底层源码
  • 找个小网站做熟食的网站美食网站