Java List最值操作全解析:从Collections.max到Top K问题优化
1. 从一次性能排查说起为什么需要关注List中的最值那天下午线上监控突然报警一个核心接口的响应时间从平时的50ms飙升至了2秒。经过一番紧急排查问题定位到了一个看似简单的功能上从一份包含数万个用户积分变动的ListInteger中实时找出当前积分最高的前10名用户进行展示。最初的代码直接使用了Collections.max()遍历整个列表每次请求都执行一次。在用户量激增、列表数据膨胀后这个O(n)的遍历操作在频繁的接口调用下瞬间成了性能瓶颈。这个案例让我意识到即便像“获取List中的最大值最小值”这样基础的操作在不同的场景、不同的数据规模下也藏着不少门道。它不仅是面试八股文里的常客更是日常开发中影响代码效率与稳定性的关键细节。今天我们就抛开简单的API调用深入聊聊在Java中处理List集合最值的各种姿势、背后的原理以及如何根据实际场景做出最优选择。2. 基础方法回顾与原理剖析当我们拿到一个List想要求其中的最大值或最小值时最直观的几种方法是什么它们各自又是如何工作的2.1 使用Collections.max()与Collections.min()这是标准库提供的最直接的工具方法。对于初学者或者追求代码简洁性的场景这通常是首选。import java.util.*; public class BasicDemo { public static void main(String[] args) { ListInteger numbers Arrays.asList(12, 5, 8, 20, 1, 15); // 获取最大值 Integer max Collections.max(numbers); System.out.println(最大值: max); // 输出: 最大值: 20 // 获取最小值 Integer min Collections.min(numbers); System.out.println(最小值: min); // 输出: 最小值: 1 } }原理剖析Collections.max(Collection? extends T coll)方法内部本质上是一次线性遍历。它会使用集合的迭代器Iterator逐个访问元素。关键在于第二个参数——一个Comparator比较器。对于像Integer这样实现了Comparable接口的类可以不传Comparator方法内部会使用元素的自然顺序进行比较。其核心逻辑可以简化为以下伪代码public static T extends Object Comparable? super T T max(Collection? extends T coll) { Iterator? extends T i coll.iterator(); T candidate i.next(); // 先取第一个元素作为候选 while (i.hasNext()) { T next i.next(); if (next.compareTo(candidate) 0) // 如果下一个元素更大 candidate next; // 更新候选值 } return candidate; }时间复杂度O(n)需要遍历整个集合一次。空间复杂度O(1)只使用了固定数量的临时变量。注意使用这个方法有一个重要前提集合不能为空。如果传入一个空的List比如Collections.max(new ArrayList())会抛出NoSuchElementException。在实际编码中务必做好空集合或null的判断。2.2 使用Java 8 Stream API对于已经使用Java 8及以上版本的开发者Stream API提供了一种更函数式、更现代的写法。import java.util.*; public class StreamDemo { public static void main(String[] args) { ListInteger numbers Arrays.asList(12, 5, 8, 20, 1, 15); // 使用Stream获取最大值 OptionalInteger maxOptional numbers.stream().max(Integer::compareTo); maxOptional.ifPresent(max - System.out.println(Stream最大值: max)); // 使用Stream获取最小值 OptionalInteger minOptional numbers.stream().min(Integer::compareTo); minOptional.ifPresent(min - System.out.println(Stream最小值: min)); // 更简洁的写法结合orElse处理空值 Integer maxValue numbers.stream().max(Integer::compareTo).orElse(null); Integer minValue numbers.stream().min(Integer::compareTo).orElse(0); // 默认值0 } }原理与选择Stream.max()和Stream.min()是终止操作它们内部也是通过归约reduce操作实现的本质上仍然是遍历整个流。其返回类型是OptionalT这是相比Collections工具方法的一大进步。Optional强制你思考和处理“不存在”的情况避免了潜在的NoSuchElementException。那么Collections.max()和Stream.max()该如何选择代码风格如果你在函数式编程或链式调用中Stream更自然、更流畅。空安全Stream返回Optional更安全、更现代。性能在单纯的遍历求最值场景下两者性能差异微乎其微。但Stream有额外的开销如创建流对象在极端性能敏感且循环次数巨大的情况下Collections方法可能略有优势但这种差异在99%的应用中可以忽略不计。可读性对于简单的操作Collections.max()一目了然对于需要结合filter、map等中间操作的复杂处理Stream的可读性更好。2.3 手动遍历最原始也最灵活有时你可能需要在一次遍历中同时找到最大值和最小值或者需要记录最大值对应的索引或其他关联信息。这时手动遍历是唯一的选择。public class ManualTraversal { public static void main(String[] args) { ListInteger numbers Arrays.asList(12, 5, 8, 20, 1, 15); if (numbers null || numbers.isEmpty()) { System.out.println(列表为空); return; } // 初始化最大值和最小值为第一个元素 int max numbers.get(0); int min numbers.get(0); int maxIndex 0; int minIndex 0; // 从第二个元素开始遍历 for (int i 1; i numbers.size(); i) { Integer current numbers.get(i); if (current max) { max current; maxIndex i; } if (current min) { min current; minIndex i; } } System.out.println(最大值: max , 索引: maxIndex); System.out.println(最小值: min , 索引: minIndex); } }为什么选择手动遍历效率一次遍历同时求出最大值和最小值时间复杂度依然是O(n)但常数项更优比分别调用max()和min()节省一次完整遍历。信息丰富你可以轻松记录下标、关联的对象或者执行更复杂的比较逻辑。完全控制你可以处理任何边界情况定义特殊的比较规则。3. 进阶场景与性能考量基础方法足以应对大部分情况但软件开发中总有更复杂的需求。当数据量变大、操作变频繁或者数据结构变复杂时我们就需要更深入的思考。3.1 处理复杂对象集合实际业务中我们处理的很少是简单的Integer列表更多的是对象列表。例如一个ListUser我们需要根据用户的年龄或积分来找到最大/最小值。class User { private String name; private int age; private double score; // 构造方法、getter、setter省略... // 自定义比较器按年龄比较 public static ComparatorUser ageComparator Comparator.comparingInt(User::getAge); // 自定义比较器按积分比较 public static ComparatorUser scoreComparator Comparator.comparingDouble(User::getScore); } public class ComplexObjectDemo { public static void main(String[] args) { ListUser users Arrays.asList( new User(Alice, 25, 88.5), new User(Bob, 30, 92.0), new User(Charlie, 22, 85.0) ); // 方法1使用Collections.max/min传入自定义比较器 User oldestUser Collections.max(users, User.ageComparator); User highestScoreUser Collections.max(users, User.scoreComparator); // 方法2使用Stream API同样传入比较器 OptionalUser youngestUser users.stream().min(User.ageComparator); OptionalUser lowestScoreUser users.stream().min(User.scoreComparator); // 方法3手动遍历实现更复杂的逻辑例如找到积分最高且年龄大于25的用户 User targetUser null; for (User user : users) { if (user.getAge() 25) { if (targetUser null || user.getScore() targetUser.getScore()) { targetUser user; } } } } }关键点对于自定义对象比较器Comparator是核心。Java 8的Comparator.comparing系列方法可以非常简洁地创建比较器。确保你的比较逻辑满足自反性、对称性和传递性否则在排序或求最值时可能出现不可预料的结果。3.2 大数据量下的性能陷阱与优化回到开头的那个性能问题。当List非常大例如数十万、数百万元素并且最值查询非常频繁时简单的O(n)遍历每次都会成为负担。优化思路一空间换时间——维护有序集合如果数据是静态的或者插入/删除操作远少于查询操作一个根本的优化思路是让数据始终保持有序。使用TreeSet或PriorityQueue在数据初始化时就将其放入TreeSet自动排序的Set或作为最大/最小堆的PriorityQueue。这样获取最大值或最小值的时间复杂度是O(1)或O(log n)。缺点会破坏原有List的顺序如果使用TreeSet还会去重且插入删除成本变高O(log n)。适用于“构建一次查询多次”的场景。// 使用PriorityQueue构建最大堆 PriorityQueueInteger maxHeap new PriorityQueue(Collections.reverseOrder()); maxHeap.addAll(numbers); Integer maxFromHeap maxHeap.peek(); // O(1)获取最大值 // 注意poll()会移除堆顶元素peek()只查看不移除优化思路二缓存结果如果数据变化不频繁但最值查询频繁可以缓存计算结果。惰性计算与缓存在数据变更时增、删、改标记缓存失效。在查询时如果缓存有效则直接返回否则重新计算并更新缓存。这是一种经典的优化模式。实现要点需要注意缓存的线程安全性和失效策略。优化思路三并行流处理对于计算密集型且数据量极大的求最值操作可以考虑使用并行流parallelStream()利用多核优势。OptionalInteger max hugeList.parallelStream().max(Integer::compareTo);注意并行流不是银弹。它本身有线程调度、结果合并的开销。只有当数据量足够大通常建议在1万以上且每个元素的比较操作不是极度轻量时才能体现出优势。对于简单的整数比较数据量不够大时串行流可能更快。务必通过基准测试如JMH来验证。3.3 处理空值与边界情况健壮的程序必须考虑边界。以下是几个常见的坑点空集合Collections.max/min会抛NoSuchElementException。务必先判断list ! null !list.isEmpty()。集合中包含null元素如果List中允许存在null无论是Collections.max/min还是Stream.max/min使用自然排序时都会在比较时抛出NullPointerException。需要在放入集合前过滤null或者使用能处理null的比较器Comparator.nullsFirst/Comparator.nullsLast。并行流中的状态干扰如果流中的元素是可变对象并且在并行计算过程中被修改会导致不确定的结果甚至错误。// 处理可能包含null的列表 ListInteger listWithNulls Arrays.asList(1, null, 3, 2); // 错误会抛出NPE // Integer max Collections.max(listWithNulls); // 正确使用能处理null的比较器 ComparatorInteger nullsLastComparator Comparator.nullsLast(Integer::compareTo); Integer maxSafe Collections.max(listWithNulls, nullsLastComparator); System.out.println(maxSafe); // 输出: 3 // 或者使用Stream先过滤 Integer maxFiltered listWithNulls.stream() .filter(Objects::nonNull) .max(Integer::compareTo) .orElse(null);4. 从“求最值”到“求Top K”问题很多时候我们的需求不是单一的“最大”或“最小”而是“最大的K个”或“最小的K个”元素。这被称为Top K问题是面试和实际应用中的高频题目。4.1 基于排序的简单解法最直观的想法是排序后取前K个。public ListInteger getTopKBySorting(ListInteger list, int k, boolean largest) { if (list null || list.size() k) { return new ArrayList(list); // 如果数量不够返回全部或抛异常 } ListInteger sorted new ArrayList(list); if (largest) { sorted.sort(Collections.reverseOrder()); // 降序取前K个最大 } else { Collections.sort(sorted); // 升序取前K个最小 } return sorted.subList(0, k); }时间复杂度O(n log n)主要开销在排序上。当n远大于k时这种方法做了很多无用功我们并不关心第k名之后的顺序。4.2 基于堆PriorityQueue的高效解法这是解决Top K问题的标准高效方案时间复杂度为O(n log k)在n很大、k较小时优势明显。求最小的K个数最大的K个数同理 思路是维护一个最大堆堆顶是堆中最大的元素。遍历列表当堆大小小于k时直接加入当堆大小等于k时如果当前元素比堆顶小则弹出堆顶当前最大的加入当前元素。遍历结束后堆中剩下的就是最小的k个数。public ListInteger getSmallestK(ListInteger list, int k) { if (list null || list.size() k) { return new ArrayList(list); } // 最大堆 PriorityQueueInteger maxHeap new PriorityQueue(k, Collections.reverseOrder()); for (Integer num : list) { if (maxHeap.size() k) { maxHeap.offer(num); } else if (num maxHeap.peek()) { maxHeap.poll(); // 移除当前堆中最大的 maxHeap.offer(num); // 加入更小的 } } // 将堆中元素转为列表 ListInteger result new ArrayList(maxHeap); // 注意直接转出来的列表不一定有序如果需要有序可以再排序复杂度O(k log k) Collections.sort(result); return result; }为什么用最大堆求最小K个数因为最大堆的堆顶是“候选区”里最大的数。任何比堆顶大的数都不可能进入最小的K个数的集合因为已经有K个比它小的候选了。任何比堆顶小的数都值得替换掉当前这个“最大”的候选。求最大的K个数则需要维护一个最小堆堆顶最小逻辑与上述相反。4.3 快速选择算法快速选择算法是快速排序的变种用于在未排序的列表中找到第K小或第K大的元素。平均时间复杂度为O(n)最坏情况为O(n²)但通过随机化选取基准可以避免最坏情况。找到第K小的元素后其左边的所有元素就是最小的K个数但无序。public int quickSelect(ListInteger list, int k) { // 实现略涉及递归和分区操作 // 核心思想随机选一个基准将数组分为小于基准和大于基准的两部分。 // 如果基准的位置正好是k则返回。 // 如果基准位置大于k则在左边部分继续找。 // 如果基准位置小于k则在右边部分继续找。 return 0; }快速选择算法通常用于只需要“第K个”元素值或者对找到的Top K元素顺序没有要求的场景。它的优势是平均时间复杂度低且是原地算法空间复杂度O(1)。选择哪种方案K很小n很大优先使用堆方法O(n log k)尤其是当内存中无法容纳整个排序列表时。需要结果有序如果对Top K的结果要求有序且K不大排序后取子列表O(n log n)或堆方法最后排序O(n log k k log k)都可以考虑。只需要第K个的值不关心其他快速选择算法平均O(n)是最优选择。数据流场景数据逐个到来堆方法是唯一选择因为它可以在遍历过程中动态维护Top K。5. 实战中的经验、陷阱与扩展思考掌握了核心方法我们还需要一些“踩坑”得来的经验才能写出真正健壮的代码。5.1 关于比较的深度陷阱Comparable vs Comparator这是面试常考点也是容易出错的地方。Comparable自然排序定义在类的内部表示对象自身固有的排序规则。比如Integer、String。实现compareTo(T o)方法。Comparator比较器定义在类的外部是一种“策略”可以根据不同需求提供多种排序规则。比如按年龄排、按分数排。陷阱1违反约定compareTo和compare方法必须满足以下约定否则在使用TreeSet、Collections.sort或求最值时可能出错sgn(x.compareTo(y)) -sgn(y.compareTo(x))对称性可传递性(x.compareTo(y)0 y.compareTo(z)0)蕴含x.compareTo(z)0x.compareTo(y)0应蕴含sgn(x.compareTo(z)) sgn(y.compareTo(z))一个常见的错误是在比较时只考虑了部分字段导致两个对象a和ba.compareTo(b)0但a.equals(b)false这可能会影响一些集合的行为。陷阱2整型溢出在实现自定义比较器特别是比较两个整型差值时直接做减法可能导致溢出。// 错误示例可能溢出 ComparatorInteger badComparator (a, b) - a - b; // 当aInteger.MAX_VALUE, b-1时 a-b 会溢出变成负数导致比较结果错误 // 正确示例使用标准比较方法 ComparatorInteger goodComparator1 Integer::compare; ComparatorInteger goodComparator2 Comparator.comparingInt(i - i);5.2 并行计算的“坑”虽然并行流可以加速但它并非没有代价。线程安全确保你的List源是线程安全的如CopyOnWriteArrayList或者在并行计算过程中不会被修改。更常见的是直接从一个普通的ArrayList创建并行流只要在流操作期间不修改源列表就是安全的。性能开销并行流有拆分、调度、合并的开销。对于非常小的列表比如几百个元素串行流更快。有状态Lambda表达式避免在流操作中使用会修改外部状态如非原子类计数器的Lambda这会导致数据竞争。// 错误示例有状态的Lambda ListInteger list IntStream.range(0, 10000).boxed().collect(Collectors.toList()); int[] unsafeCounter new int[1]; list.parallelStream().forEach(i - unsafeCounter[0]); // 结果不确定 // 正确做法使用无状态操作或线程安全的归约 long count list.parallelStream().count(); // 使用内置的终止操作 int sum list.parallelStream().mapToInt(i-i).sum(); // 使用归约操作5.3 扩展到其他集合类型List是有序、可重复的集合。那其他集合呢Set如HashSet, TreeSetCollections.max/min和StreamAPI同样适用。对于TreeSet由于其本身有序直接使用first()和last()方法获取最小最大值效率最高O(1)或O(log n)。Map求Map中Value的最大值需要先将values()转为集合或流。MapString, Integer map ...; OptionalMap.EntryString, Integer maxEntry map.entrySet() .stream() .max(Map.Entry.comparingByValue());数组对于基本类型数组如int[]可以使用Arrays.stream(array)将其转为流再操作或者手动遍历。对于对象数组同样适用。5.4 内存与效率的终极权衡在极端场景下比如列表大到无法一次性装入内存例如几十GB的日志文件如何求最大值 这时我们需要使用外部排序或流式处理的思路。分块读取将大文件分割成多个能装入内存的小块。局部求最值对每个小块在内存中求出最大值。归并最值比较所有小块的最大值得到全局最大值。这个过程实际上是MapReduce思想的雏形。在Java中我们可以利用Files.lines()返回流或自定义缓冲读取器结合流式处理实现类似的效果避免内存溢出OutOfMemoryError。从一个小小的“求List最值”出发我们探讨了从基础API到高级算法从单线程到并行处理从内存计算到流式处理的完整知识链。核心思想是没有最好的方法只有最适合当前场景的方法。在写下一行Collections.max()之前不妨先花几秒钟思考一下数据规模、性能要求、以及未来的扩展性这或许就是资深工程师与普通开发者的细微差别所在。

相关新闻

WebGIS开发入门到进阶 | 高德地图打卡功能实现教程

WebGIS开发入门到进阶 | 高德地图打卡功能实现教程

前面我们学习了监听地图的 click 事件,实现了在地图上点击新增热门标记点的功能。那么这节前面我们学习了监听地图的 click 事件,实现了在地图上点击新增热门标记点的功能。那么这节课,我们利用上一节 GeoJSON 数据持久化来实现标记点的保存功…

2026/7/30 9:09:43 阅读更多 →
2026年揭秘!余姚专业靠谱、备受称赞的雾化香水喷头公司究竟哪家强?

2026年揭秘!余姚专业靠谱、备受称赞的雾化香水喷头公司究竟哪家强?

在化妆品包材领域,雾化香水喷头作为关键组件,其质量、性能和供应服务至关重要。对于化妆品包材采购商而言,找到一家专业靠谱的供应商并非易事。在余姚,众多的供应商让人眼花缭乱,而宁波微旺塑业有限公司凭借着多方面的…

2026/7/30 9:09:43 阅读更多 →
Matlab路径规划:10分钟从零创建栅格地图(附完整代码)

Matlab路径规划:10分钟从零创建栅格地图(附完整代码)

1. 项目概述:为什么从栅格地图开始?如果你刚接触路径规划,无论是做机器人导航、游戏AI寻路,还是自动驾驶的仿真测试,第一个拦路虎往往不是复杂的A*或Dijkstra算法,而是“地图”。没有一张清晰、可计算的地图…

2026/7/30 9:09:43 阅读更多 →

最新新闻

AI 总是胡说八道?一文讲透 RAG,让大模型告别“瞎编“

AI 总是胡说八道?一文讲透 RAG,让大模型告别“瞎编“

AI 总是胡说八道?一文讲透 RAG,让大模型告别"瞎编"你问 ChatGPT 公司报销流程,它给你编了一套不存在的制度 你问它最新政策,它说"我的知识截止到 2024 年" 你让它基于内部文档写报告,它根本不知道…

2026/7/30 9:16:45 阅读更多 →
Logistic回归:从原理推导到手写实现与Scikit-learn实战

Logistic回归:从原理推导到手写实现与Scikit-learn实战

1. 项目概述:从“分类”这个根本问题说起在机器学习的浩瀚海洋里,我们最先学会游泳的地方,往往是预测一个连续的值,比如房价、销量,这被称为回归。但现实世界充满了选择与判断:这封邮件是不是垃圾邮件&…

2026/7/30 9:16:45 阅读更多 →
WeChatPad终极指南:一键解锁微信平板模式,实现真正的双设备同步登录

WeChatPad终极指南:一键解锁微信平板模式,实现真正的双设备同步登录

WeChatPad终极指南:一键解锁微信平板模式,实现真正的双设备同步登录 【免费下载链接】WeChatPad 强制使用微信平板模式 项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad 还在为微信无法同时在手机和平板上登录而烦恼吗?WeChat…

2026/7/30 9:15:45 阅读更多 →
WLCSP晶圆级芯片封装技术:原理、挑战与选型实战指南

WLCSP晶圆级芯片封装技术:原理、挑战与选型实战指南

1. 项目概述:为什么WLCSP是当下芯片封装的“显学”?最近几年,但凡和芯片沾点边的工程师,无论是做设计的、搞工艺的,还是跑市场的,都绕不开一个词:WLCSP。它全称是Wafer Level Chip Scale Packag…

2026/7/30 9:15:45 阅读更多 →
Linux USB PHY驱动深度解析:从物理层原理到内核框架实战

Linux USB PHY驱动深度解析:从物理层原理到内核框架实战

1. 项目概述:为什么从USB PHY开始聊驱动搞Linux驱动开发,尤其是USB这块,很多朋友一上来就扎进usbcore、hub.c或者各种Gadget、Host Controller驱动里,对着复杂的协议状态机和海量的结构体发懵。我刚开始也是这么过来的&#xff0c…

2026/7/30 9:15:45 阅读更多 →
主流固定资产管理系统深度解析:企业如何精准选型?

主流固定资产管理系统深度解析:企业如何精准选型?

在企业运营过程中,固定资产管理是保障生产经营有序开展的重要环节。传统人工管理模式易出现账实不符、盘点效率低、资产闲置等问题,而数字化的固定资产管理系统能有效解决这些痛点。市面上系统类型丰富,企业可根据自身需求选择适配方案&#…

2026/7/30 9:15:45 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击: https://intelliparadigm.com 第一章:AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具,它并非替代教师,而是通过语义理解、知识图谱与多模态生成能力,将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻