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

从零开始掌握JAVA集合框架:Set与Map的核心用法解析

1. 初识Java集合框架:Set与Map的定位

很多刚接触Java的小伙伴第一次看到集合框架时都会有点懵——ArrayList、LinkedList、HashSet、HashMap这么多类到底该用哪个?我刚开始学Java时也踩过这个坑,直到老师用了个特别形象的比喻:集合框架就是个容器王国,List像排队买奶茶的队伍(有序可重复),Set像高中学号系统(无序不重复),而Map则像你的微信好友列表(键值对存储)。今天我们就重点聊聊后两位成员:Set和Map。

Set和Map在实际开发中使用频率超高。比如我做过的电商项目中,用HashSet存储用户收藏的商品ID(自动去重),用HashMap存储购物车数据(商品ID为键,数量为值)。它们最核心的区别在于:

  • Set:纯值集合,相当于只有Key的Map
  • Map:键值对集合,通过Key快速定位Value

这里有个新手容易混淆的点:虽然HashSet和HashMap名字相似,但HashSet底层其实就是个HashMap!当你调用hashSet.add("元素")时,系统实际执行的是hashMap.put("元素", PRESENT),这个PRESENT是个固定的Object对象。这个设计非常巧妙,既复用代码又满足去重需求。

2. 玩转HashSet:去重小能手

2.1 基础操作三步走

先来看个实际案例:统计一篇文章中出现的所有英文单词。如果用List存储,需要手动判断重复,而HashSet直接搞定:

Set<String> words = new HashSet<>(); Scanner sc = new Scanner(new File("article.txt")); while(sc.hasNext()) { words.add(sc.next().toLowerCase()); // 自动去重 } System.out.println("共出现"+words.size()+"个不同单词");

HashSet的常规操作特别简单:

  1. 创建集合Set<String> set = new HashSet<>()
  2. 添加元素set.add("Java")(重复元素返回false)
  3. 判断存在set.contains("Python")

有个坑我踩过:自定义对象去重要重写equals和hashCode方法。比如学生管理系统要按学号去重:

class Student { int id; String name; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Student)) return false; return id == ((Student) o).id; } @Override public int hashCode() { return Objects.hash(id); } }

2.2 底层原理揭秘

HashSet的去重魔法其实靠的是HashMap的键唯一性。当添加元素时:

  1. 先计算hash值确定存储位置
  2. 如果该位置为空,直接存入
  3. 如果已有元素,用equals比较内容
  4. 内容相同则视为重复,拒绝加入

这里有个性能优化点:好的hashCode应该让不同对象尽量分散。比如学生类如果用姓名hashCode,同姓的同学都会挤在同一个位置(哈希冲突),而学号通常分布更均匀。

3. HashMap深度解析:键值对的艺术

3.1 基础操作实战

HashMap特别适合做缓存,比如我做过的天气查询系统:

Map<String, Weather> cache = new HashMap<>(); // 查询前先查缓存 Weather weather = cache.get("北京"); if (weather == null) { weather = queryFromAPI(); // 耗时操作 cache.put("北京", weather); // 存入缓存 }

常用方法一览:

  • put(key, value):添加键值对(重复key会覆盖)
  • get(key):按键索值(不存在返回null)
  • remove(key):删除指定键值对
  • keySet():获取所有键的集合
  • values():获取所有值的集合

3.2 高级特性剖析

HashMap的扩容机制是个面试常考点。默认初始容量16,加载因子0.75,当元素超过16×0.75=12时就会扩容。扩容不是简单的数组变大,而是重建哈希表:

  1. 新建双倍大小的数组
  2. 重新计算每个元素的位置(非常耗时)
  3. 链表长度>8且数组长度>64时,链表转红黑树

我曾遇到过HashMap性能问题:初始化时知道要存1万条数据,却用默认容量,导致多次扩容。正确做法:

Map<String, Data> map = new HashMap<>(16384); // 16384=2^14 > 10000/0.75

3.3 遍历方式对比

HashMap遍历有几种姿势,性能差异明显:

// 方法1:先取keySet再遍历(效率较低) for (String key : map.keySet()) { System.out.println(key + "=" + map.get(key)); } // 方法2:直接遍历entrySet(推荐) for (Map.Entry<String, Data> entry : map.entrySet()) { System.out.println(entry.getKey() + "=" + entry.getValue()); } // 方法3:Java8的forEach map.forEach((k, v) -> System.out.println(k + "=" + v));

实测10万次遍历,entrySet比keySet快约30%,因为避免了重复查找。

4. 实战应用案例精讲

4.1 统计字符出现次数

这个经典面试题完美展示HashMap的威力:

String str = "programming"; Map<Character, Integer> count = new HashMap<>(); for (char c : str.toCharArray()) { count.put(c, count.getOrDefault(c, 0) + 1); } // 输出:{p=1, r=2, o=1, g=2, a=1, m=2, i=1, n=1}

4.2 实现简单缓存系统

结合软引用(SoftReference)可以打造内存敏感的缓存:

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>(); void loadImage(String url) { SoftReference<Bitmap> ref = imageCache.get(url); Bitmap bitmap = ref != null ? ref.get() : null; if (bitmap == null) { bitmap = downloadImage(url); // 耗时网络请求 imageCache.put(url, new SoftReference<>(bitmap)); } // 使用bitmap... }

4.3 集合运算妙用

利用Set可以轻松实现数学集合运算:

Set<Integer> set1 = new HashSet<>(Arrays.asList(1,2,3)); Set<Integer> set2 = new HashSet<>(Arrays.asList(2,3,4)); // 并集 set1.addAll(set2); // [1,2,3,4] // 交集 set1.retainAll(set2); // [2,3] // 差集 set1.removeAll(set2); // [1]

在权限系统中,这种操作特别有用,比如合并用户角色权限。

http://www.jsqmd.com/news/575316/

相关文章:

  • 山海鲸公有云 vs 私有云,一篇帮你彻底选明白
  • 告别第三方库!用Qt5自制高颜值仪表控件(电压表/油表/码盘),轻松集成到你的项目
  • HarmonyOS6 ArkTS Grid 以当前行最高的GridItem的高度为其他GridItem的高度
  • Phi-3-mini-4k-instruct-gguf快速部署:7860端口网页服务+独立venv隔离环境实录
  • 深入I.MX RT1170 MIPI DSI显示框架:剖析LCDIFv2驱动层与影子寄存器机制
  • 别再只会双击打开了!Simulink模型文件的5种打开方式与隐藏技巧(2021b版)
  • d2s-editor:开源工具解决暗黑破坏神2存档管理难题的完整方案
  • Phi-3-mini-4k-instruct-gguf完整指南:模型路径校验+代理配置清理+镜像固化
  • 基于嵌入向量的智能检索!HOOPS AI 解锁 CAD 零件相似性搜索新方式
  • 讲讲蓝深集团盈利能力如何,产品性价比高吗在杭州地区 - myqiye
  • AI应用上线前必须验证的7类流式异常:断连重试失败、Token乱序、Content-Type错配、内存泄漏…FastAPI 2.0官方测试套件首次公开
  • CAPL脚本避坑指南:Signal Wait函数返回值处理与超时逻辑的5个常见错误
  • WindowResizer终极指南:3个简单步骤解决Windows窗口尺寸限制难题
  • STC89C52RC + HX711 + JQ8400-FL:手把手教你做一个能说话的5KG电子秤(附完整代码和PCB)
  • 如何在自己的ai编程agent添加沙箱环境
  • SenseVoice Small GPU推理参数详解:batch_size/VAD阈值/断句灵敏度调优
  • 海外仓库存数据怎么处理?库存数据不准确及账实不符解决方案! - 跨境小媛
  • Matlab R2024a硬件支持包安装避坑指南:以Arduino为例(附离线包下载)
  • 技术解析:Cursor Pro功能的激活方法与技术实现
  • 手机续航的秘密武器:深入拆解LPDDR4的低功耗特性(VDDQ/TCSR/PASR)
  • YOLOv8小目标检测不给力?试试这个ASF-YOLO特征融合魔改方案(附消融实验)
  • Qt实战:5分钟搞定LineEdit和TextEdit的回车发送功能(附完整代码)
  • Vue3 与第三方组件库联动:Element Plus 按需引入与二次封装
  • 编译原理(龙书):从理论到实践——解析编译器与解释器的核心差异
  • 实战演练:基于autoclaw利用快马平台快速开发可部署的任务管理看板
  • 漫画脸描述生成新手教程:零基础生成可商用二次元角色设计方案
  • Django DEBUG=False时如何安全查看错误详情?3种不暴露敏感信息的方法
  • 从零到一:基于Docker Compose构建ThinkPHP 8.1微服务化开发栈
  • 算力驱动智慧零售|腾视科技AI边缘算力盒子 —— 无人商超全场景解决方案重磅发布
  • 别再用if-else了!用状态机重构你的51单片机红外循迹小车代码(思路+代码对比)