HashMap有几种遍历方法?推荐使用哪种?
mhr18 2024-12-06 17:53 20 浏览 0 评论
本文已收录《面试题精选》系列,Gitee 开源地址:https://gitee.com/mydb/interview
HashMap 的遍历方法有很多种,不同的 JDK 版本有不同的写法,其中 JDK 8 就提供了 3 种 HashMap 的遍历方法,并且一举打破了之前遍历方法“很臃肿”的尴尬。
1.JDK 8 之前的遍历
JDK 8 之前主要使用 EntrySet 和 KeySet 进行遍历,具体实现代码如下。
1.1 EntrySet 遍历
EntrySet 是早期 HashMap 遍历的主要方法,其实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
以上程序的执行结果,如下图所示:
1.2 KeySet 遍历
KeySet 的遍历方式是循环 Key 内容,再通过 map.get(key) 获取 Value 的值,具体实现如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
for (String key : map.keySet()) {
System.out.println(key + ":" + map.get(key));
}
}
以上程序的执行结果,如下图所示:
KeySet 性能问题
通过以上代码,我们可以看出使用 KeySet 遍历,其性能是不如 EntrySet 的,因为 KeySet 其实循环了两遍集合,第一遍循环是循环 Key,而获取 Value 有需要使用 map.get(key),相当于有循环了一遍集合,所以 KeySet 循环不能建议使用,因为循环了两次,效率比较低。
1.3 EntrySet 迭代器遍历
EntrySet 和 KeySet 除了以上直接循环外,我们还可以使用它们的迭代器进行循环,如 EntrySet 的迭代器实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
以上程序的执行结果,如下图所示:
1.4 KeySet 迭代器遍历
KeySet 也可以使用迭代器的方式进行遍历,实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
System.out.println(key + ":" + map.get(key));
}
}
以上程序的执行结果,如下图所示:
虽然 KeySet 循环方式不推荐使用,但还是有必要了解一下的。
1.5 迭代器的作用
既然能直接遍历,那为什么还要用迭代器呢?通过以下例子我们就知道了。
不使用迭代器删除
如果不使用迭代器,假如我们在遍历 EntrySet 时,在遍历代码中删除元素,代码的实现如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
if ("Java".equals(entry.getKey())) {
// 删除此项
map.remove(entry.getKey());
continue;
}
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
以上程序的执行结果,如下图所示:
可以看到,如果在遍历的代码中动态删除元素,非迭代器的方式就会报错。
使用迭代器删除
接下来,我们使用迭代器循环 EntrySet,并且在循环中动态删除元素,实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
if ("Java".equals(entry.getKey())) {
// 删除此项
iterator.remove();
continue;
}
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
以上程序的执行结果,如下图所示:
从上述结果可以看出,使用迭代器的优点是可以在循环的时候,动态的删除集合中的元素。而上面非迭代器的方式则不能在循环的过程中删除元素(程序会报错)。
2.JDK 8 之后的遍历
在 JDK 8 之后 HashMap 的遍历就变得方便很多了,JDK 8 中包含了以下 3 种遍历方法:
- 使用 Lambda 遍历
- 使用 Stream 单线程遍历
- 使用 Stream 多线程遍历
我们分别来看。
2.1 Lambda 遍历
使用 Lambda 表达式的遍历方法实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
}
以上程序的执行结果,如下图所示:
2.2 Stream 单线程遍历
Stream 遍历是先得到 map 集合的 EntrySet,然后再执行 forEach 循环,实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
map.entrySet().stream().forEach((entry) -> {
System.out.println(entry.getKey() + ":" + entry.getValue());
});
}
以上程序的执行结果,如下图所示:
2.3 Stream 多线程遍历
Stream 多线程的遍历方式和上一种遍历方式类似,只是多执行了一个 parallel 并发执行的方法,此方法会根据当前的硬件配置生成对应的线程数,然后再进行遍历操作,实现代码如下:
public static void main(String[] args) {
// 创建并赋值 hashmap
HashMap<String, String> map = new HashMap() {{
put("Java", " Java Value.");
put("MySQL", " MySQL Value.");
put("Redis", " Redis Value.");
}};
// 循环遍历
map.entrySet().stream().parallel().forEach((entry) -> {
System.out.println(entry.getKey() + ":" + entry.getValue());
});
}
以上程序的执行结果,如下图所示:
注意上述图片的执行结果,可以看出当前执行结果和之前的所有遍历结果都不一样(打印元素的顺序不一样),因为程序是并发执行的,所以没有办法保证元素的执行顺序和打印顺序,这就是并发编程的特点。
推荐使用哪种遍历方式?
不同的场景推荐使用的遍历方式是不同的,例如,如果是 JDK 8 之后的开发环境,推荐使用 Stream 的遍历方式,因为它足够简洁;而如果在遍历的过程中需要动态的删除元素,那么推荐使用迭代器的遍历方式;如果在遍历的时候,比较在意程序的执行效率,那么推荐使用 Stream 多线程遍历的方式,因为它足够快。所以这个问题的答案是不固定的,我们需要知道每种遍历方法的优缺点,再根据不同的场景灵活变通。
总结
本文介绍了 7 种 HashMap 的遍历方式,其中 JDK 8 之前主要使用 EntrySet 和 KeySet 的遍历方式,而 KeySet 的遍历方式性能比较低,一般不推荐使用。然而在 JDK 8 之后遍历方式就有了新的选择,可以使用比较简洁的 Lambda 遍历,也可以使用性能比较高的 Stream 多线程遍历。
是非审之于己,毁誉听之于人,得失安之于数。
博主介绍:80 后程序员,写博客这件事“坚持”了 11 年,爱好:读书、慢跑、羽毛球。
相关推荐
- Java面试宝典之问答系列(java面试回答)
-
以下内容,由兆隆IT云学院就业部根据多年成功就业服务经验提供:1.写出从数据库表Custom中查询No、Name、Num1、Num2并将Name以姓名显示、计算出的和以总和显示的SQL。SELECT...
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
-
如上图的数据容灾架构下,上层应用如果使用Druid连接池,应该如何配置,才能在数据库集群节点切换甚至主备数据中心站点切换的情况下,上层应用不需要变动(无需修改配置也无需重启);即数据库节点宕机/...
- SpringBoot多数据源dynamic-datasource快速入门
-
一、简介dynamic-datasourc是一个基于SpringBoot的快速集成多数据源的启动器,其主要特性如下:支持数据源分组,适用于多种场景纯粹多库读写分离一主多从混合模式。支持...
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
-
项目简介及系统架构JeecgBoot是一款基于SpringBoot的开发平台,它采用前后端分离架构,集成的框架有SpringBoot2.x、SpringCloud、AntDesignof...
- 常见文件系统格式有哪些(文件系统类型有哪几种)
-
PART.01常见文件系统格式有哪些常见的文件系统格式有很多,通常根据使用场景(Windows、Linux、macOS、移动设备、U盘、硬盘等)有所不同。以下是一些主流和常见的文件系统格式及其特点:一...
- Oracle MySQL Operator部署集群(oracle mysql group by)
-
以下是使用OracleMySQLOperator部署MySQL集群的完整流程及关键注意事项:一、部署前准备安装MySQLOperator通过Helm安装Operator到Ku...
- LibreOffice加入"转向Linux"运动
-
LibreOffice项目正准备削减部分Windows支持,并鼓励用户切换到Linux系统。自Oracle放弃OpenOffice后,支持和指导LibreOffice开发的文档基金会对未来有着明确的观...
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
-
IT之家6月28日消息,科技媒体linuxiac昨日(6月27日)发布博文,报道称OracleLinux10正式发布,完全二进制兼容(binarycompatibility...
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
-
项目介绍支持插件化、可视化的数据异构中间件,支持的数据异构方式如下MySQL<——>MySQL(增量、全量)MySQL<——>Oracle(增量、全量)Oracle...
- oracle关于xml的解析(oracle读取xml节点的属性值)
-
有时需要在存储过程中处理xml,oracle提供了相应的函数来进行处理,xmltype以及相关的函数。废话少说,上代码:selectxmltype(SIConfirmOutput).extract...
- 如何利用DBSync实现数据库同步(通过dblink同步数据库)
-
DBSync是一款通用型的数据库同步软件,能侦测数据表之间的差异,能实时同步差异数据,从而使双方始终保持一致。支持各种数据库,支持异构同步、增量同步,且提供永久免费版。本文介绍其功能特点及大致用法,供...
- MYSQL存储引擎InnoDB(八十):InnoDB静态数据加密
-
InnoDB支持独立表空间、通用表空间、mysql系统表空间、重做日志和撤消日志的静态数据加密。从MySQL8.0.16开始,还支持为模式和通用表空间设置加密默认值,这允许DBA控制在这些模...
- JDK高版本特性总结与ZGC实践(jdk高版本兼容低版本吗)
-
美团信息安全技术团队核心服务升级JDK17后,性能与稳定性大幅提升,机器成本降低了10%。高版本JDK与ZGC技术令人惊艳,且JavaAISDK最低支持JDK17。本文总结了JDK17的主要...
- 4 种 MySQL 同步 ES 方案,yyds!(两个mysql数据库自动同步的方法)
-
本文会先讲述数据同步的4种方案,并给出常用数据迁移工具,干货满满!不BB,上文章目录:1.前言在实际项目开发中,我们经常将MySQL作为业务数据库,ES作为查询数据库,用来实现读写分离,...
- 计算机Java培训课程包含哪些内容?其实就这六大块
-
不知不觉秋天已至,如果你还处于就业迷茫期,不如来学习Java。对于非科班小白来说,Java培训会更适合你。提前了解下计算机Java培训课程内容,会有助于你后续学习。下面,我就从六个部分为大家详细介绍...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Java面试宝典之问答系列(java面试回答)
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
- SpringBoot多数据源dynamic-datasource快速入门
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
- 常见文件系统格式有哪些(文件系统类型有哪几种)
- Oracle MySQL Operator部署集群(oracle mysql group by)
- LibreOffice加入"转向Linux"运动
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
- oracle关于xml的解析(oracle读取xml节点的属性值)
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)