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

枚举缓存工具

此文章为笔记,为阅读其他文章的感受、补充、记录、练习、汇总,非原创,感谢每个知识分享者。

文章目录

  • 1. 背景
  • 2. 枚举缓存
  • 3. 样例展示
  • 4. 性能对比
  • 5. 总结

本文通过几种样例展示如何高效优雅的使用java枚举消除冗余代码。

1. 背景

枚举在系统中的地位不言而喻,状态、类型、场景、标识等等,少则十几个多则上百个,相信以下这段代码很常见,而且类似的代码到处都是,目标:消除这类冗余代码。

/*** 根据枚举代码获取枚举* */public static OrderStatus getByCode(String code){for (OrderStatus v : values()) {if (v.getCode().equals(code)) {return v;}}return null;}/*** 根据枚举名称获取枚举* 当枚举内的实例数越多时性能越差*/public static OrderStatus getByName(String name){for (OrderStatus v : values()) {if (v.name().equals(name)) {return v;}}return null;}

2. 枚举缓存

  • 减少代码冗余,代码简洁
  • 去掉for循环,性能稳定高效

模块设计图

图片
在这里插入图片描述

缓存结构

图片

源码分析
源码展示

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 枚举缓存*/
public class EnumCache {/*** 以枚举任意值构建的缓存结构**/static final Map<Class<? extends Enum>, Map<Object, Enum>> CACHE_BY_VALUE = new ConcurrentHashMap<>();/*** 以枚举名称构建的缓存结构**/static final Map<Class<? extends Enum>, Map<Object, Enum>> CACHE_BY_NAME = new ConcurrentHashMap<>();/*** 枚举静态块加载标识缓存结构*/static final Map<Class<? extends Enum>, Boolean> LOADED = new ConcurrentHashMap<>();/*** 以枚举名称构建缓存,在枚举的静态块里面调用** @param clazz* @param es* @param <E>*/public static <E extends Enum> void registerByName(Class<E> clazz, E[] es) {Map<Object, Enum> map = new ConcurrentHashMap<>();for (E e : es) {map.put(e.name(), e);}CACHE_BY_NAME.put(clazz, map);}/*** 以枚举转换出的任意值构建缓存,在枚举的静态块里面调用** @param clazz* @param es* @param enumMapping* @param <E>*/public static <E extends Enum> void registerByValue(Class<E> clazz, E[] es, EnumMapping<E> enumMapping) {if (CACHE_BY_VALUE.containsKey(clazz)) {throw new RuntimeException(String.format("枚举%s已经构建过value缓存,不允许重复构建", clazz.getSimpleName()));}Map<Object, Enum> map = new ConcurrentHashMap<>();for (E e : es) {Object value = enumMapping.value(e);if (map.containsKey(value)) {throw new RuntimeException(String.format("枚举%s存在相同的值%s映射同一个枚举%s.%s", clazz.getSimpleName(), value, clazz.getSimpleName(), e));}map.put(value, e);}CACHE_BY_VALUE.put(clazz, map);}/*** 从以枚举名称构建的缓存中通过枚举名获取枚举** @param clazz* @param name* @param defaultEnum* @param <E>* @return*/public static <E extends Enum> E findByName(Class<E> clazz, String name, E defaultEnum) {return find(clazz, name, CACHE_BY_NAME, defaultEnum);}/*** 从以枚举转换值构建的缓存中通过枚举转换值获取枚举** @param clazz* @param value* @param defaultEnum* @param <E>* @return*/public static <E extends Enum> E findByValue(Class<E> clazz, Object value, E defaultEnum) {return find(clazz, value, CACHE_BY_VALUE, defaultEnum);}private static <E extends Enum> E find(Class<E> clazz, Object obj, Map<Class<? extends Enum>, Map<Object, Enum>> cache, E defaultEnum) {Map<Object, Enum> map = null;if ((map = cache.get(clazz)) == null) {executeEnumStatic(clazz);// 触发枚举静态块执行map = cache.get(clazz);// 执行枚举静态块后重新获取缓存}if (map == null) {String msg = null;if (cache == CACHE_BY_NAME) {msg = String.format("枚举%s还没有注册到枚举缓存中,请在%s.static代码块中加入如下代码 : EnumCache.registerByName(%s.class, %s.values());",clazz.getSimpleName(),clazz.getSimpleName(),clazz.getSimpleName(),clazz.getSimpleName());}if (cache == CACHE_BY_VALUE) {msg = String.format("枚举%s还没有注册到枚举缓存中,请在%s.static代码块中加入如下代码 : EnumCache.registerByValue(%s.class, %s.values(), %s::getXxx);",clazz.getSimpleName(),clazz.getSimpleName(),clazz.getSimpleName(),clazz.getSimpleName(),clazz.getSimpleName());}throw new RuntimeException(msg);}if(obj == null){return defaultEnum;}Enum result = map.get(obj);return result == null ? defaultEnum : (E) result;}private static <E extends Enum> void executeEnumStatic(Class<E> clazz) {if (!LOADED.containsKey(clazz)) {synchronized (clazz) {if (!LOADED.containsKey(clazz)) {try {// 目的是让枚举类的static块运行,static块没有执行完是会阻塞在此的Class.forName(clazz.getName());LOADED.put(clazz, true);} catch (Exception e) {throw new RuntimeException(e);}}}}}/*** 枚举缓存映射器函数式接口*/@FunctionalInterfacepublic interface EnumMapping<E extends Enum> {/*** 自定义映射器** @param e 枚举* @return 映射关系,最终体现到缓存中*/Object value(E e);}}

关键解读

开闭原则

什么是开闭原则?
对修改是封闭的,对新增扩展是开放的。为了满足开闭原则,这里设计成有枚举主动注册到缓存,而不是有缓存主动加载枚举,这样设计的好处就是:当增加一个枚举时只需要在当前枚举的静态块中自主注册即可,不需要修改其他的代码
比如我们现在要新增一个状态类枚举:

public enum StatusEnum {INIT("I", "初始化"),PROCESSING("P", "处理中"),SUCCESS("S", "成功"),FAIL("F", "失败");private String code;private String desc;StatusEnum(String code, String desc) {this.code = code;this.desc = desc;}public String getCode() {return code;}public String getDesc() {return desc;}static {// 通过名称构建缓存,通过EnumCache.findByName(StatusEnum.class,"SUCCESS",null);调用能获取枚举EnumCache.registerByName(StatusEnum.class, StatusEnum.values());// 通过code构建缓存,通过EnumCache.findByValue(StatusEnum.class,"S",null);调用能获取枚举EnumCache.registerByValue(StatusEnum.class, StatusEnum.values(), StatusEnum::getCode);}
}

注册时机

将注册放在静态块中,那么静态块什么时候执行呢?
1、当第一次创建某个类的新实例时
2、当第一次调用某个类的任意静态方法时
3、当第一次使用某个类或接口的任意非final静态字段时
4、当第一次Class.forName时
如果我们入StatusEnum创建枚举,那么在应用系统启动的过程中StatusEnum的静态块可能从未执行过,则枚举缓存注册失败,
所有我们需要考虑延迟注册,代码如下:
private static <E extends Enum> void executeEnumStatic(Class<E> clazz) {if (!LOADED.containsKey(clazz)) {synchronized (clazz) {if (!LOADED.containsKey(clazz)) {try {// 目的是让枚举类的static块运行,static块没有执行完是会阻塞在此的Class.forName(clazz.getName());LOADED.put(clazz, true);} catch (Exception e) {throw new RuntimeException(e);}}}}}

Class.forName(clazz.getName())被执行的两个必备条件:

1、缓存中没有枚举class的键,也就是说没有执行过枚举向缓存注册的调用,见EnumCache.find方法对executeEnumStatic方法的调用;
2、executeEnumStatic中的LOADED.put(clazz, true);还没有被执行过,也就是Class.forName(clazz.getName());没有被执行过;

我们看到executeEnumStatic中用到了双重检查锁,所以分析一下正常情况下代码执行情况和性能:

1、当静态块还未执行时,大量的并发执行find查询。
此时executeEnumStatic中synchronized会阻塞其他线程;第一个拿到锁的线程会执行Class.forName(clazz.getName());同时触发枚举静态块的同步执行;之后其他线程会逐一拿到锁,第二次检查会不成立跳出executeEnumStatic;2、当静态块已经执行,且静态块里面正常执行了缓存注册,大量的并发执行find查询。
executeEnumStatic方法不会调用,没有synchronized引发的排队问题;3、当静态块已经执行,但是静态块里面没有调用缓存注册,大量的并发执行find查询。
find方法会调用executeEnumStatic方法,但是executeEnumStatic的第一次检查通不过;
find方法会提示异常需要在静态块中添加注册缓存的代码;总结:第一种场景下会有短暂的串行,但是这种内存计算短暂串行相比应用系统的业务逻辑执行是微不足道的,
也就是说这种短暂的串行不会成为系统的性能瓶颈

3. 样例展示

构造枚举

public enum StatusEnum {INIT("I", "初始化"),PROCESSING("P", "处理中"),SUCCESS("S", "成功"),FAIL("F", "失败");private String code;private String desc;StatusEnum(String code, String desc) {this.code = code;this.desc = desc;}public String getCode() {return code;}public String getDesc() {return desc;}static {// 通过名称构建缓存,通过EnumCache.findByName(StatusEnum.class,"SUCCESS",null);调用能获取枚举EnumCache.registerByName(StatusEnum.class, StatusEnum.values());// 通过code构建缓存,通过EnumCache.findByValue(StatusEnum.class,"S",null);调用能获取枚举EnumCache.registerByValue(StatusEnum.class, StatusEnum.values(), StatusEnum::getCode);}
}

测试类

public class Test{public static void main(String [] args){System.out.println(EnumCache.findByName(StatusEnum.class, "SUCCESS", null));// 返回默认值StatusEnum.INITSystem.out.println(EnumCache.findByName(StatusEnum.class, null, StatusEnum.INIT));// 返回默认值StatusEnum.INITSystem.out.println(EnumCache.findByName(StatusEnum.class, "ERROR", StatusEnum.INIT));System.out.println(EnumCache.findByValue(StatusEnum.class, "S", null));// 返回默认值StatusEnum.INITSystem.out.println(EnumCache.findByValue(StatusEnum.class, null, StatusEnum.INIT));// 返回默认值StatusEnum.INITSystem.out.println(EnumCache.findByValue(StatusEnum.class, "ERROR", StatusEnum.INIT));}
}

执行结果

SUCCESS
INIT
INIT
SUCCESS
INIT
INIT

4. 性能对比

对比代码,如果OrderType中的实例数越多性能差异会越大

public class Test {enum OrderType {_00("00", "00"),_01("01", "01"),_02("02", "02"),_03("03", "03"),_04("04", "04"),_05("05", "05"),_06("06", "06"),_07("07", "07"),_08("08", "08"),_09("09", "09"),_10("10", "10");private String code;private String desc;OrderType(String code, String desc) {this.code = code;this.desc = desc;}public String getCode() {return code;}public String getDesc() {return desc;}static {EnumCache.registerByValue(OrderType.class, OrderType.values(), OrderType::getCode);}public static OrderType getEnumByCode(String code, OrderType def) {OrderType[] values = OrderType.values();for (OrderType value : values) {if (value.getCode().equals(code)) {return value;}}return def;}}private static final OrderType DEF = OrderType._00;private static final int TIMES = 10000000;static void compare(String code) {long s = System.currentTimeMillis();for (int idx = 0; idx < TIMES; idx++) {OrderType.getEnumByCode(code, DEF);}long t = System.currentTimeMillis() - s;System.out.println(String.format("枚举->%s : %s", code, t));s = System.currentTimeMillis();for (int idx = 0; idx < TIMES; idx++) {EnumCache.findByValue(OrderType.class, code, DEF);}t = System.currentTimeMillis() - s;System.out.println(String.format("缓存->%s : %s", code, t));System.out.println();}public static void main(String[] args) throws Exception {for (int idx = 0; idx < 2; idx++) {compare("NotExist");for (OrderType value : OrderType.values()) {compare(value.getCode());}System.out.println("=================");}}
}

执行结果

枚举->NotExist : 312
缓存->NotExist : 105枚举->00 : 199
缓存->00 : 164枚举->01 : 313
缓存->01 : 106枚举->02 : 227
缓存->02 : 90枚举->03 : 375
缓存->03 : 92枚举->04 : 260
缓存->04 : 92枚举->05 : 272
缓存->05 : 78枚举->06 : 284
缓存->06 : 78枚举->07 : 315
缓存->07 : 76枚举->08 : 351
缓存->08 : 78枚举->09 : 372
缓存->09 : 81枚举->10 : 402
缓存->10 : 78=================
枚举->NotExist : 199
缓存->NotExist : 68枚举->00 : 99
缓存->00 : 91枚举->01 : 141
缓存->01 : 79枚举->02 : 178
缓存->02 : 77枚举->03 : 202
缓存->03 : 77枚举->04 : 218
缓存->04 : 81枚举->05 : 259
缓存->05 : 90枚举->06 : 322
缓存->06 : 78枚举->07 : 318
缓存->07 : 78枚举->08 : 347
缓存->08 : 77枚举->09 : 373
缓存->09 : 79枚举->10 : 404
缓存->10 : 78=================

5. 总结

1、代码简洁;
2、枚举中实例数越多,缓存模式的性能优势越多;

相关文章:

枚举缓存工具

此文章为笔记&#xff0c;为阅读其他文章的感受、补充、记录、练习、汇总&#xff0c;非原创&#xff0c;感谢每个知识分享者。 文章目录 1. 背景2. 枚举缓存3. 样例展示4. 性能对比5. 总结 本文通过几种样例展示如何高效优雅的使用java枚举消除冗余代码。 1. 背景 枚举在系统…...

【BASH】回顾与知识点梳理(二十五)

【BASH】回顾与知识点梳理 二十五 二十五. 特殊shell、PAM 模块、讯息传递和大量建置账号25.1 特殊shell特殊的 shell, /sbin/nologin 25.2 PAM模块25.3 Linux 主机上的用户讯息传递查询使用者&#xff1a; w, who, last, lastlog使用者对谈&#xff1a; write, mesg, wall使用…...

什么是Node js?什么是React?有什么区别

JavaScript是当今最流行的编程语言之一&#xff0c;它用于开发多种技术&#xff0c;两种这样的技术是Node.js和React。许多学生很难理解Nodejs和React之间的区别。 React和Nodejs之间的主要区别在于它们的使用位置。Nodejs 用于开发应用程序的服务器端&#xff0c;而Reactjs用于…...

使用postman做接口测试

1.接口测试&#xff1a;针对软件对外提供服务的接口的输入输出进行测试&#xff0c;以及接口间相互逻辑的测试&#xff0c;验证接口功能与接口描述文档的一致性 2.接口测试流程&#xff1a; 1&#xff09;获取接口信息&#xff1a;通过接口文档或抓包来获取接口的基本调用方式和…...

VMware Workstation 如何启用复制粘贴

产品&#xff1a;VMware Workstation 16 Pro 版本&#xff1a;16.1.1 build-17801498 我们刚安装好的 VMware Workstation 会发现无法复制粘贴文件到虚拟机中&#xff0c;如下为解决方案&#xff1a; 1.点击 虚拟机&#xff0c;点击 安装 VMware Tools(T)...。 2.虚拟机下面会…...

免费小程序商城搭建之b2b2c o2o 多商家入驻商城 直播带货商城 电子商务b2b2c o2o 多商家入驻商城 直播带货商城 电子商务 bbc

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端…...

VSCode-Python传参数进行Debug

新建demo.py import argparse def parse_args():description "debug example" parser argparse.ArgumentParser(descriptiondescription) help "The path of address"parser.add_argument(--host,help help) parser.add_ar…...

实践-传统深度学习

简介与安装 2 训练自己的数据集整体流程3 数据加载与预处理4 搭建网络模型5 学习率对结果的影响6 Drop-out操作7 权重初始化方法对比8 初始化标准差对结果的影响9 正则化对结果的影响10 加载模型进行测试 TensorFlow&#xff1a;每一步都需要自己做。 Keras&#xff1a;做起来更…...

爬虫:使用Selenium模拟人工操作及获取网页内容

专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础)》 再推荐一下最近热更的:《大厂测试高频面试题详解》 该专栏对…...

AOP开发

目录 1、简介 1.1、AOP 相关概念 1.2、AOP 开发明确的事项 1.3、知识要点 2、两种方式 3、基于 XML 3.1、快速入门 3.1.1、导入坐标 3.1.2、创建接口和实现类 3.1.3、创建切面 3.1.4、配置bean 3.1.5、配置织入 3.1.6、测试 3.2、切点表达式 3.2.1、表达式举例 …...

Streamlit项目: 轻松搭建部署个人博客网站

文章目录 1 前言1.1 探索 Streamlit&#xff1a;轻松创建交互式应用1.2 最全 Streamlit 教程专栏 2 我的个人博客网站已上线&#xff01;2.1 一个集成了智能中医舌诊-中e诊专栏的博客网站2.2 前期准备2.3 使用 Streamlit Cloud 运行 3 知识点讲解3.1 实现多页面&#xff1a;两种…...

手把手教你如何实现内网搭建电影网站并进行公网访问(保姆级教学)

手把手教你如何实现内网搭建电影网站并进行公网访问 文章目录 手把手教你如何实现内网搭建电影网站并进行公网访问前言1. 把软件分别安装到本地电脑上1.1 打开PHPStudy软件&#xff0c;安装一系列电影网站所需的支持软件1.2 设置MacCNS10的运行环境1.3 进入电影网页的安装程序1…...

Redis_事务操作

13. redis事务操作 13.1事务简介 原子性(Atomicity) 一致性(Consistency) 隔离性(isolation) 持久性(durabiliby) ACID 13.2 Redis事务 提供了multi、exec命令来完成 第一步&#xff0c;客户端使用multi命令显式地开启事务第二步&#xff0c;客户端把事务中要执行的指令发…...

python质检工具(pylint)安装使用总结

1、Pylint Pylint工具主要类似java中的checkStyle和findbugs,是检查代码样式和逻辑规范的工具。 1.1、Pylint安装流程: 打开PyCharm软件,打开如图1.1所示Terminal终端窗口,先查看python版本和pip版本,pip是19.0.3,python是2.7 图1.1 运行pip install pylint安装pylin…...

“深入探究JVM:解密Java虚拟机的工作原理“

标题&#xff1a;深入探究JVM&#xff1a;解密Java虚拟机的工作原理 摘要&#xff1a;本文将深入探究Java虚拟机&#xff08;JVM&#xff09;的工作原理&#xff0c;包括JVM的组成部分、类加载过程、内存管理、垃圾回收机制以及即时编译器等。通过了解JVM的工作原理&#xff0…...

同济子豪兄模板 半天搞定图像分类

同济子豪兄模板 半天搞定图像分类 ‘’import cv2 import numpy as np import time from tqdm import tqdm 视频逐帧处理代码模板 不需修改任何代码&#xff0c;只需定义process_frame函数即可 def generate_video(input_path‘videos/robot.mp4’): filehead input_path.…...

接口自动化测试,Fiddler使用抓包辅助实战,一篇彻底打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、快捷设置&…...

概念解析 | 隐式神经表示:揭开神经网络黑盒的奥秘

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:隐式神经表示(Implicit Neural Representations) 隐式神经表示:揭开神经网络黑盒的奥秘 近年来,神经网络在各种任务上取得了惊人的进步,但其内部表示方式依然难以解读,被称为“…...

深入浅出PHP封装根据商品ID获取淘宝商品详情数据方法

要通过淘宝的API获取商品详情&#xff0c;您可以使用淘宝开放平台提供的接口来实现。以下是一种使用PHP编程语言实现的示例&#xff0c;展示如何通过淘宝开放平台API获取商品详情&#xff1a; 首先&#xff0c;确保您已注册成为淘宝开放平台的开发者&#xff0c;并创建一个应用…...

自动切换HTTP爬虫ip助力Python数据采集

在Python数据采集中&#xff0c;如果你需要爬取一些网站的数据&#xff0c;并且需要切换IP地址来避免被封或限制&#xff0c;我们可以考虑以下几种方式来实现自动切换HTTP爬虫IP。 1. 使用代理服务器 使用代理服务器是常见的IP切换技术之一。你可以购买或使用免费的代理服务器…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...

Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一&#xff1a;HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二&#xff1a;Floyd 快慢指针法&#xff08;…...