List、Set的相似性

1. 在 MyBatis 的mapper.xml文件中,List 和 Set 的遍历方式是完全一样的

这是因为 MyBatis 的<foreach>标签在底层处理时,并不严格区分具体的集合类型(ListSet等),它统一将它们视为一个Iterable对象(或数组)进行处理。

核心机制

<foreach>标签的核心工作是:遍历一个可迭代的对象。无论是List还是Set,都实现了java.lang.Iterable接口,因此 MyBatis 可以用同一套逻辑来遍历它们。

使用示例与对比

假设你的 Mapper 接口方法如下:

// 参数是 ListList<User>selectUsersByIdList(List<Integer>idList);// 参数是 SetList<User>selectUsersByIdSet(Set<Integer>idSet);

mapper.xml中,对应的 SQL 写法是一模一样的:

<selectid="selectUsersByIdList"resultType="User">SELECT * FROM user WHERE id IN<foreachcollection="idList"item="id"open="("separator=","close=")">#{id}</foreach></select><selectid="selectUsersByIdSet"resultType="User">SELECT * FROM user WHERE id IN<foreachcollection="idSet"item="id"open="("separator=","close=")">#{id}</foreach></select>

它们的区别仅在于 Mapper 接口方法参数名不同(idListvsidSet),以及在 XML 中collection属性引用的名字不同。<foreach>标签的内部处理逻辑没有任何区别。

关键注意事项

虽然遍历方式相同,但有几个细节需要注意:

项目说明通用建议
参数名<foreach>中的collection属性值必须与接口方法中的参数名严格一致。使用明确的参数名,避免用listcollection等默认名。
参数唯一性当接口方法有且仅有一个集合参数时,在 XML 中可以直接用其参数名引用。如果方法有多个参数,务必使用@Param(“key”)注解显式命名。
有序性List是有序的,遍历出的顺序与存入顺序一致;Set(如HashSet)通常无序。如果在 SQL 中依赖遍历顺序,请务必使用List
去重List允许重复元素;Set会自动去重。如果希望 IN 语句中的条件自动去重,优先使用Set
空集合两者都可能为空。需在代码或 SQL 中处理空集合,否则可能导致 SQL 语法错误。在 Java 代码或 MyBatis 的<if>标签中判空是良好实践。

总结与建议

  • 遍历方式完全一样。无需因为ListSet而改变 XML 中的写法。
  • 选择依据:根据你的业务需求来选择:
    • 需要保持顺序允许重复,用List
    • 需要自动去重且不关心顺序,用Set

2. List.size() 和 Set.size()排除重复数据后是一样的

在 Java 中,idList.size()idSet.size()不一定一样。这取决于原始数据中是否有重复元素

简单来说:

  • 如果原始数据没有重复,它们的size()相等
  • 如果原始数据有重复List会保留所有重复项,而Set会自动去重,此时Listsize()大于Setsize()

核心区别对比

特性List(如ArrayList)Set(如HashSet)size()的影响
是否允许重复元素允许不允许(自动去重)决定size()是否相同的关键
顺序有序(保持插入顺序)通常无序(HashSetsize()无关
size()方法含义返回列表中元素的总数(包含重复项)返回集合中唯一元素的数量计算基数不同

示例说明

importjava.util.*;publicclassTest{publicstaticvoidmain(String[]args){// 原始数据有重复:1, 2, 2, 3List<Integer>idList=Arrays.asList(1,2,2,3);Set<Integer>idSet=newHashSet<>(idList);// 会自动去重System.out.println("List size: "+idList.size());// 输出:4System.out.println("Set size: "+idSet.size());// 输出:3System.out.println("size 是否相等: "+(idList.size()==idSet.size()));// 输出:false// 在MyBatis的SQL中,这会导致:// List -> WHERE id IN (1, 2, 2, 3) // 条件有4个,但重复条件可能多余// Set -> WHERE id IN (1, 2, 3) // 条件只有3个,是精确的唯一条件集合}}

在你的 MyBatis 场景下的影响

结合你之前关于 MyBatis 的问题,这个区别会产生直接的实际影响:

  • 使用List:如果传入的idList包含重复的用户ID,生成的 SQL 会是IN (1, 2, 2, 3)。虽然结果正确(数据库会对重复ID返回相同行,结果集不变),但SQL语句更长,可能影响解析效率,且不符合语义
  • 使用Set:如果传入的idSet(或先将List转为Set),生成的 SQL 会是IN (1, 2, 3)SQL更精简、语义更准确,是更推荐的做法。

最佳实践建议

  1. 数据一致性:如果业务上“ID集合不应重复”,建议在接口层直接使用Set类型,或在使用List前进行去重,从源头上保证数据语义。
  2. 性能考量:对于contains()操作(检查某个ID是否存在),HashSet(时间复杂度接近 O(1))的性能远高于ArrayList(O(n))。如果后续有此类操作,Set是更好选择。
  3. 明确选择
    • 需要保持顺序明确允许重复时,用List
    • 需要确保唯一性去重或进行快速查找时,用Set

所以,当你不确定传入的ID集合是否有重复时,如果你希望生成的SQL条件是最精简、准确的唯一值,在MyBatis的Mapper接口参数中直接使用Set类型是更安全、更语义化的选择

3. List去重,由Set可以优化

1.避免重复遍历(最优方案)

使用Collectors.toSet()直接收集到 Set,只需一次遍历:

// 方案1:使用 Collectors.toSet()longsectionCount=similarProjectVOS.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).collect(Collectors.toSet()).size();// 方案2:使用 toCollection 指定 HashSet(可控制初始容量)longsectionCount=similarProjectVOS.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).collect(Collectors.toCollection(HashSet::new)).size();

2.并行流优化(大数据量时)

当列表非常大(例如超过10万条)时,可考虑并行流:

longsectionCount=similarProjectVOS.parallelStream().map(PvModuleVO.SimilarProjectVO::getSectionId).collect(Collectors.toSet()).size();

3.根据实际情况选择数据结构

如果只需要判断是否有重复,不需要具体数量,用distinct().count() > 1即可:

booleanhasDuplicates=similarProjectVOS.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).distinct().count()>1;

📊 性能对比分析

方案时间复杂度空间复杂度适用场景
原方案.distinct().count()O(n)O(k)(k为去重后数量)小到中等数据集
优化方案.collect(Collectors.toSet()).size()O(n)O(k)推荐:所有场景
并行流方案O(n/p)(p为线程数)O(k)大数据集(>10万条)
传统 for 循环O(n)O(k)极致性能要求

💡 实际测试对比

// 性能测试代码示例List<PvModuleVO.SimilarProjectVO>data=generateTestData(100000);// 原方案longstart1=System.nanoTime();longcount1=data.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).distinct().count();longtime1=System.nanoTime()-start1;// 优化方案longstart2=System.nanoTime();longcount2=data.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).collect(Collectors.toSet()).size();longtime2=System.nanoTime()-start2;System.out.printf("原方案: %d 个, 耗时: %.2f ms%n",count1,time1/1e6);System.out.printf("优化方案: %d 个, 耗时: %.2f ms%n",count2,time2/1e6);

🔍 进一步优化建议

  1. 源头去重:如果similarProjectVOS的数据来自数据库查询,考虑在 SQL 层面直接去重计数:

    SELECTCOUNT(DISTINCTsection_id)FROM...WHERE...
  2. 考虑空值处理:如果sectionId可能为 null,需要处理:

    longsectionCount=similarProjectVOS.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).filter(Objects::nonNull)// 过滤 null 值.collect(Collectors.toSet()).size();
  3. 预分配容量:如果知道大概的去重后数量,可以优化 HashSet 初始容量:

    intestimatedSize=similarProjectVOS.size()/2;// 预估容量longsectionCount=similarProjectVOS.stream().map(PvModuleVO.SimilarProjectVO::getSectionId).collect(Collectors.toCollection(()->newHashSet<>(estimatedSize))).size();

🎯 总结推荐

最佳实践:使用.collect(Collectors.toSet()).size(),原因:

  1. 语义更清晰:明确表达"获取不重复的集合"
  2. 性能更优:避免了distinct()的中间状态维护
  3. 代码简洁:一行完成,易于阅读

如果你的数据量特别大(百万级以上),或者这是性能关键路径,建议先进行基准测试,选择最适合你场景的方案。

4. 打印效果是一样的

简单来说,最直观的区别是:List 严格按插入顺序保留所有元素(包括重复项);而典型的 Set(如 HashSet)不保证顺序且自动去重。它们的toString()输出格式通常也不同(List 是[a, b, c],Set 是[a, c, b]等)。

核心区别对比

特性List(如ArrayList)Set(如HashSet)对打印输出的影响
顺序性严格保持插入顺序通常不保证顺序HashSet基于哈希值)最常见区别:打印顺序可能不同
重复元素保留所有重复项自动去重(只保留一个)有重复数据时,List打印更长
toString()格式格式相同,都为[元素1, 元素2, ...]格式相同,都为[元素1, 元素2, ...]格式一样,但括号内内容不同

示例演示

importjava.util.*;publicclassPrintDemo{publicstaticvoidmain(String[]args){// 原始数据:有重复,注意插入顺序List<Integer>idList=newArrayList<>(Arrays.asList(3,1,2,1,4));Set<Integer>idSet=newHashSet<>(idList);// 用List构造Set会自动去重System.out.println("idList.toString() = "+idList);// 输出:idList.toString() = [3, 1, 2, 1, 4] ← 保持顺序和重复System.out.println("idSet.toString() = "+idSet);// 可能输出:idSet.toString() = [1, 2, 3, 4] ← 顺序不定,已去重// 也可能是:[3, 1, 2, 4] 等,取决于HashSet内部实现// 用迭代器/for循环打印,差异更明显System.out.print("遍历idList: ");for(Integerid:idList){System.out.print(id+" ");}// 输出:遍历idList: 3 1 2 1 4System.out.print("\n遍历idSet : ");for(Integerid:idSet){System.out.print(id+" ");}// 可能输出:遍历idSet : 1 2 3 4}}

三种关键场景分析

场景List 打印Set 打印输出是否“相同”
数据完全无重复[A, B, C][A, B, C][B, C, A]元素集合相同,但顺序可能不同
数据有重复[A, B, B, C][A, B, C]元素集合不同(数量、内容)
使用有序Set
(如TreeSet,LinkedHashSet)
[A, B, C]TreeSet: 排序后[A, B, C]
LinkedHashSet: 保持插入顺序[A, B, C]
可能相同(见下文)

特殊说明:如果你使用LinkedHashSet,它会保持插入顺序;如果使用TreeSet,它会按自然排序。对于无重复且顺序匹配的数据,它们的打印可能和 List“看起来一样”。

在 MyBatis 场景下的实际影响

回到你最初关心的 MyBatis 使用场景,这个差异会直接反映在生成的 SQL 中:

-- 假设原始数据:List<Integer> ids = Arrays.asList(3, 1, 2, 1, 4)-- 使用 List 参数WHEREidIN(3,1,2,1,4)-- 顺序固定,有重复-- 使用 HashSet 参数WHEREidIN(1,2,3,4)-- 顺序可能改变,已去重-- 使用 LinkedHashSet 参数(用 new LinkedHashSet<>(list) 构造)WHEREidIN(3,1,2,4)-- 保持原List顺序,但去重

实用建议

  1. 调试时:不要依赖Set(特别是HashSet)的toString()顺序来判断数据正确性。
  2. 需要顺序一致
    • 使用List
    • 或使用LinkedHashSet(去重但保序)
    • 或使用TreeSet(去重且排序)
  3. 在 MyBatis 中
    • 如果 SQL 条件顺序不影响结果(如IN子句),可忽略顺序差异。
    • 但如果需要稳定可预测的 SQL(如调试日志),建议在接口层统一转换为LinkedHashSetTreeSet

总结idListidSet打印出的数据通常不一样(顺序、重复项)。如果你希望它们“看起来一样”,需要确保数据无重复,并使用LinkedHashSetTreeSet来保序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1160645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RAGFlow 全面接入 MinerU 2.0,支持 pipeline、vlm-transformers、vlm-sglang 三种模式,解析精度大幅度up

重磅升级&#xff01;RAGFlow全面接入MinerU 2.0&#xff1a;三大模式解锁复杂文档解析新高度在企业级RAG&#xff08;检索增强生成&#xff09;落地过程中&#xff0c;“文档解析精度”始终是决定应用效果的核心瓶颈——扫描件的模糊文本识别错误、表格结构还原失真、多模态内…

一文彻底搞懂算力计算:CPU与GPU的核心方法全解析

在AI大模型训练/推理、高性能计算&#xff08;HPC&#xff09;、数据中心运维等场景中&#xff0c;“算力”是衡量硬件性能的核心指标&#xff0c;也是方案选型、成本评估的关键依据。但很多开发者对算力的理解仅停留在“TOPS”“FLOPS”等名词层面&#xff0c;不清楚其具体计算…

突然发现 AI Agent设计思路真的好清晰

AI Agent 作为具备自主行为能力的智能体&#xff0c;其系统架构设计对功能实现与性能表现至关重要。本文基于 LLM 的AI Agent系统架构设计&#xff0c;分四部分展开。 &#x1f4cd;系统架构 系统架构&#xff0c;有三层。工具层像个采购员&#xff0c;从外面的 API、数据库啥的…

终于有人把数据血缘说明白了

在与众多同行和读者的交流中&#xff0c;笔者察觉到“数据血缘”这一概念被频繁提及&#xff0c;然而大多数人对其背后的深层价值、技术实现路径以及可能遇到的难题&#xff0c;还缺乏深刻的理解。简单来说&#xff0c;掌握数据血缘&#xff0c;可以在数据出现问题时迅速找到根…

论文与期刊的级别

在科技项目申报、人才评价等场景中&#xff0c;论文和期刊的级别划分主要依据期刊的学术影响力、收录范围、主管单位等维度&#xff0c;不同地区和行业的认定标准略有差异&#xff0c;以下是通用的分级体系及说明&#xff1a;一、 期刊的常见级别划分期刊的级别是判定论文学术价…

24家科技巨头参与,美国“创世纪计划”有什么样的野心?

2025年11 月 24 日&#xff0c;特朗普在白宫正式签署行政令&#xff0c;启动了被外界称为 “AI 版曼哈顿计划 ” 的 “ 创世纪计划 ” &#xff08; The Genesis Mission &#xff09;。“创世纪”一词源自《圣 经》&#xff0c;特朗普为这项国家级AI任务冠以此名&#xff0c;足…

sql 如果字段为空就用另一个字段

您可以使用 COALESCE() 函数 或 CASE WHEN 表达式来处理这个逻辑&#xff1a; 方案一&#xff1a;使用 COALESCE() 函数&#xff08;推荐&#xff09; SELECTbpi.batch_id AS batchId,bpi.batch_name AS batchName,bpi.plate_name AS plateName,bpi.category AS productType,bp…

Android 基础入门教程2.5.2 GridView(网格视图)的基本使用

2.5.2 GridView(网格视图)的基本使用 分类 Android 基础入门教程 本节引言&#xff1a; 本节给大家介绍的是第二个Adapter类的控件——GridView(网格视图)&#xff0c;见名知义&#xff0c;ListView是列表&#xff0c; GridView就是显示网格&#xff01;他和ListView一样是Ab…

Android 基础入门教程2.5.3 Spinner(列表选项框)的基本使用

2.5.3 Spinner(列表选项框)的基本使用 分类 Android 基础入门教程 本节引言&#xff1a; 本来本节是想给大家介绍一个Gallery(画廊)的一个控件的&#xff0c;后来想想还是算了&#xff0c;因为 在Android 4.1后就已经被弃用了&#xff0c;尽管我们可以通过兼容不来使用Galler…

AI赋能央企数智化转型研究报告:AI赋能央企转型应用现状、AI赋能央企转型路径与挑战、AI赋能央企转型服务商体系、未来展望

本报告全面描绘了AI驱动央企数智化转型的宏大图景。其核心逻辑是&#xff1a;在国家战略强力驱动下&#xff0c;央企正以AI为核心引擎&#xff0c;通过构建自主可控的技术底座、推动AI向全业务核心场景深度渗透、并牵头构建协同创新的产业生态&#xff0c;最终实现自身高质量发…

大模型推理框架对比:SGLang 与 vLLM 的核心差异解析

在大模型推理引擎领域&#xff0c;vLLM 凭借高效的 KV 缓存管理与连续批处理技术&#xff0c;成为高并发场景的主流选择&#xff1b;而同源&#xff08;LMSYS Org&#xff09;的 SGLang 则以“结构化生成”为核心突破&#xff0c;重新定义了复杂 LLM 应用的开发范式。两者虽师出…

​ Android 基础入门教程​2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用

2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用 分类 Android 基础入门教程 本节引言&#xff1a; 本节继续来学习Adapter类的控件&#xff0c;这次带来的是AutoCompleteTextView(自动完成文本框)&#xff0c; 相信细心的你发现了&#xff0c;和Adapter搭边的控件&am…

Agent搭建-超详细教程,存一下吧

AI Agent在于其能自动执行复杂任务&#xff0c;大幅提升自动化和智能决策的效率&#xff0c;让任务自动化&#xff0c;主要包括感知、记忆、规划与决策、行动/使用工具。 AI Agent的工作原理分为几个步骤。 ✔首先是感知&#xff0c;AI Agent通过传感器感知物理或虚拟环境中的变…

事务中的隔离性是如何保证的呢?(你解释一下MVCC)

事务的隔离性通过锁和多版本并发控制&#xff08;MVCC&#xff09;来保证。MVCC通过维护数据的多个版本来避免读写冲突。底层实现包括隐藏字段、undo log和read view。隐藏字段包括trx_id和roll_pointer。undo log记录了不同版本的数据&#xff0c;通过roll_pointer形成版本链。…

数据治理到底应该怎么治理?治理什么?在哪治理?治理路径是什么?治理流程又是什么?附案例及解决方案

数据治理是对组织内数据资产的系统性管理&#xff0c;核心是建立权责清晰的组织、统一的规范&#xff08;标准、质量、安全&#xff09;以及可持续的流程。其治理对象覆盖数据的全生命周期&#xff0c;治理范围贯穿所有业务与系统。治理路径应以价值为导向&#xff0c;从关键业…

如何在liunx环境安装PageAdmin Cms系统

web系统一般建议安装在srv/wwwoot目录下&#xff0c;下面步骤演示如何安装pageadmin版本。 1、进入srv目录&#xff0c;创建wwwroot/demo目录&#xff0c;生产环境demo替换为网站标识或域名标识&#xff0c;方便后期识别。2、上传web文件zip压缩包到 /srv/wwwroot/demo目录下解…

2026常见的企业网站建设系统推荐

如今&#xff0c;网站已成为企业开展网络营销、展示品牌形象的重要载体&#xff0c;几乎各个行业的企业都会选择搭建专属官网。对于非专业背景或缺乏建站经验的企业而言&#xff0c;选择成熟的第三方 PHP 网站系统或 CMS&#xff08;内容管理系统&#xff09;框架&#xff0c;是…

既然强转会报错,java为啥不封装处理好,避免强转报错?

✅ 用【大白话 人话】彻底讲懂&#xff0c;不讲原理、只讲结论、保证听懂&#xff0c;0 基础也能明白&#xff01;你不懂太正常了&#xff0c;这个问题本身就是 Java 的反直觉坑&#xff0c;咱们抛开所有专业术语&#xff0c;只说人话、只讲你关心的「为什么」和「怎么办」&am…

帧同步游戏设置一个“固定输入延迟”它背后的逻辑

大部分帧同步游戏会设置一个“固定输入延迟”,比如: 所有操作都延迟“3~5 帧”生效。 表面意思: 你按下技能/移动,这个操作不是立刻在逻辑上生效,而是“排队”等几帧。 很多人一看就懵: “为啥要故意搞个延迟?不就更卡了吗?我明明想要手感更快的啊!” 这一篇就专门围…

mkcert 本地 HTTPS 证书全平台教程

摘要&#xff1a; 本文提供跨平台本地HTTPS证书生成方案&#xff0c;使用mkcert工具在Windows/macOS/Linux统一创建可信证书。核心步骤包括&#xff1a;1&#xff09;各平台安装mkcert&#xff1b;2&#xff09;生成证书文件&#xff08;含私钥&#xff09;&#xff1b;3&…