HashMap有几种遍历方法?推荐使用哪种?
本文已收录《面试精选》系列,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面试真题解析
HashMap有几种遍历方法?推荐使用哪种?的更多相关文章
- HashMap的四种遍历方法,及效率比较(简单明了)
https://yq.aliyun.com/ziliao/210955 public static void main(String[] args) { HashMap<Integer, Str ...
- Java中Map的三种遍历方法
Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历. 告诉您们一个小秘密: (下↓面是测试代码,最爱看 ...
- map的三种遍历方法!
map的三种遍历方法! 集合的一个很重要的操作---遍历,学习了三种遍历方法,三种方法各有优缺点~~ /* * To change this template, choose Tools | Te ...
- javase-常用三种遍历方法
javase-常用三种遍历方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public ...
- Jquery中each的三种遍历方法
Jquery中each的三种遍历方法 $.post("urladdr", { "data" : "data" }, function(dat ...
- java 完全二叉树的构建与四种遍历方法
本来就是基础知识,不能丢的太干净,今天竟然花了那么长的时间才写出来,记一下. 有如下的一颗完全二叉树: 先序遍历结果应该为:1 2 4 5 3 6 7 中序遍历结果应该为:4 2 5 ...
- Dictionary 的几种遍历方法
Dictionary 的几种遍历方法 Dictionary<string, int>dic = newDictionary<string, int>(); 方法1 foreac ...
- Java List /ArrayList 三种遍历方法
java list三种遍历方法性能比较http://www.cnblogs.com/riskyer/p/3320357.html JAVA LIST 遍历http://blog.csdn.net/lo ...
- 2017.10.25 Java List /ArrayList 三种遍历方法
java list三种遍历方法性能比较 学习java语言list遍历的三种方法,顺便测试各种遍历方法的性能,测试方法为在ArrayList中插入记录,然后遍历ArrayList,测试代码如下: pac ...
随机推荐
- Ubuntu virtualenv 创建 python3 虚拟环境 激活 退出
首先默认安装了virtualenv 创建python3虚拟环境 your-name@node-name:~/virtual_env$ virtualenv -p /usr/bin/python3 py ...
- 力扣 - 剑指 Offer 58 - I. 翻转单词顺序
题目 剑指 Offer 58 - I. 翻转单词顺序 思路1 假如题目要求我们翻转字符串,那么我们可以从末尾往前开始遍历每一个字符,同时将每一个字符添加到临时空间,最后输出临时空间的数据就完成翻转了, ...
- Linux mem 2.4 Buddy 内存管理机制
文章目录 1. Buddy 简介 2. Buddy 初始化 2.1 Struct Page 初始化 2.2 Buddy 初始化 3. 内存释放 4. 内存分配 4.1 gfp_mask 4.2 nod ...
- Pytest使用pytest-html和allure生成测试报告
Pytest-html 1.安装命令pip3 install pytest-html,如下图: 执⾏后,会在当前⽬录下⽣成 ⼀个report.html的⽂件,打开后会展示详细的测试报告,执行该命令py ...
- spring定时任务ThreadPoolTaskScheduler使用注意事项之线程池大小
背景 最近小伙伴解决了一个工单,描述为"手工推送案件无法推,提示token失效",当前工单状态为待关闭,解决方案为"东软接口不稳定造成的,东软的接口恢复正常后,问题解决& ...
- 设计模式学习-使用go实现装饰模式
装饰器模式 定义 代码实现 优点 缺点 适用范围 装饰器模式和桥接模式的区别 参考 装饰器模式 定义 装饰模式:动态的给一些对象添加额外的职责,就增加功能来说,装饰模式比生成子类更灵活. 举个栗子: ...
- 12组-Alpha冲刺-3/6
一.基本情况 队名:字节不跳动 组长博客:https://www.cnblogs.com/147258369k/p/15546442.html 小组人数:10人 二.冲刺概况汇报 侯钦凯 过去两天完成 ...
- Mac 安装 RabbitMQ 出现的问题
安装 RabbitMQ 在官网这里按照步骤来就行 出现Error: Permission denied @ apply2files - /usr/local/lib/docker/cli-plugin ...
- python递归三战:Sierpinski Triangle、Tower of Hanoi、Maze Exploring
本文已做成视频教程投稿b站(视频版相对文本版有一些改进),点击观看视频教程 本文主要通过三个实例来帮助大家理解递归(其展示动画已上传B站): 谢尔宾斯基三角形(Sierpinski Triangle) ...
- 菜鸡的Java笔记 第十三 String 类的两种实例化方法
String 类的两种实例化方法 String 类的两种实例化方式的区别 String 类对象的比较 Stirng 类对象的使用分析 /* 1.String 类的两种实例化方式的区别 ...