面试官:HashMap有几种遍历方法?推荐使用哪种?

87d6b391b6341559c81fd9614ebc120b.png

作者 | 磊哥

来源 | Java面试真题解析(ID:aimianshi666)

转载请联系授权(微信ID:GG_Stone)

HashMap 的遍历方法有很多种,不同的 JDK 版本有不同的写法,其中 JDK 8 就提供了 3 种 HashMap 的遍历方法,并且一举打破了之前遍历方法“很臃肿”的尴尬。

1.JDK 8 之前的遍历

JDK 8 之前主要使用 EntrySet 和 KeySet 进行遍历,具体实现代码如下。

1.1 EntrySet 遍历

EntrySet 是早期 HashMap 遍历的主要方法,其实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());}
}

以上程序的执行结果,如下图所示:0bf1fe6ea69bd475b011d6a636ad69ab.png

1.2 KeySet 遍历

KeySet 的遍历方式是循环 Key 内容,再通过 map.get(key) 获取 Value 的值,具体实现如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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));}
}

以上程序的执行结果,如下图所示:fa646c629ebe827a743865b47369dd9e.png

KeySet 性能问题

通过以上代码,我们可以看出使用 KeySet 遍历,其性能是不如 EntrySet 的,因为 KeySet 其实循环了两遍集合,第一遍循环是循环 Key,而获取 Value 有需要使用 map.get(key),相当于有循环了一遍集合,所以 KeySet 循环不能建议使用,因为循环了两次,效率比较低

1.3 EntrySet 迭代器遍历

EntrySet 和 KeySet 除了以上直接循环外,我们还可以使用它们的迭代器进行循环,如 EntrySet 的迭代器实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());}
}

以上程序的执行结果,如下图所示:828d4b049f492a65499331c17133b6bc.png

1.4 KeySet 迭代器遍历

KeySet 也可以使用迭代器的方式进行遍历,实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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));}
}

以上程序的执行结果,如下图所示:455d77eb97f279d5488a66a3e3663e7c.png虽然 KeySet 循环方式不推荐使用,但还是有必要了解一下的。

1.5 迭代器的作用

既然能直接遍历,那为什么还要用迭代器呢?通过以下例子我们就知道了。

不使用迭代器删除

如果不使用迭代器,假如我们在遍历 EntrySet 时,在遍历代码中删除元素,代码的实现如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());}
}

以上程序的执行结果,如下图所示:e0ec09650b69e83c37cc1d8d0e6f84ee.png可以看到,如果在遍历的代码中动态删除元素,非迭代器的方式就会报错。

使用迭代器删除

接下来,我们使用迭代器循环 EntrySet,并且在循环中动态删除元素,实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());}
}

以上程序的执行结果,如下图所示:f1cf4d0d2e0020a4dda70bafb176ceca.png从上述结果可以看出,使用迭代器的优点是可以在循环的时候,动态的删除集合中的元素。而上面非迭代器的方式则不能在循环的过程中删除元素(程序会报错)。

2.JDK 8 之后的遍历

在 JDK 8 之后 HashMap 的遍历就变得方便很多了,JDK 8 中包含了以下 3 种遍历方法:

  • 使用 Lambda 遍历

  • 使用 Stream 单线程遍历

  • 使用 Stream 多线程遍历

我们分别来看。

2.1 Lambda 遍历

使用 Lambda 表达式的遍历方法实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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);});
}

以上程序的执行结果,如下图所示:5a2ad7073d57bb4507b06e82273677db.png

2.2 Stream 单线程遍历

Stream 遍历是先得到 map 集合的 EntrySet,然后再执行 forEach 循环,实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());});
}

以上程序的执行结果,如下图所示:eb2a321a2422e99cdb50872b8e703a5f.png

2.3 Stream 多线程遍历

Stream 多线程的遍历方式和上一种遍历方式类似,只是多执行了一个 parallel 并发执行的方法,此方法会根据当前的硬件配置生成对应的线程数,然后再进行遍历操作,实现代码如下:

public static void main(String[] args) {// 创建并赋值 hashmapHashMap<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());});
}

以上程序的执行结果,如下图所示:e1b9715ef28c8007dd06ca1b6181ea0c.png注意上述图片的执行结果,可以看出当前执行结果和之前的所有遍历结果都不一样(打印元素的顺序不一样),因为程序是并发执行的,所以没有办法保证元素的执行顺序和打印顺序,这就是并发编程的特点。

推荐使用哪种遍历方式?

不同的场景推荐使用的遍历方式是不同的,例如,如果是 JDK 8 之后的开发环境,推荐使用 Stream 的遍历方式,因为它足够简洁;而如果在遍历的过程中需要动态的删除元素,那么推荐使用迭代器的遍历方式;如果在遍历的时候,比较在意程序的执行效率,那么推荐使用 Stream 多线程遍历的方式,因为它足够快。所以这个问题的答案是不固定的,我们需要知道每种遍历方法的优缺点,再根据不同的场景灵活变通。

总结

本文介绍了 7 种 HashMap 的遍历方式,其中 JDK 8 之前主要使用 EntrySet 和 KeySet 的遍历方式,而 KeySet 的遍历方式性能比较低,一般不推荐使用。然而在 JDK 8 之后遍历方式就有了新的选择,可以使用比较简洁的 Lambda 遍历,也可以使用性能比较高的 Stream 多线程遍历。

是非审之于己,毁誉听之于人,得失安之于数。

博主介绍:80 后程序员,写博客这件事“坚持”了 11 年,爱好:读书、慢跑、羽毛球。

我的公众号:Java面试真题解析

个人微信:GG_Stone,欢迎围观朋友圈,做个点赞只交。


往期推荐:

3ea52d2c4e995d20b3db5a4456c8e1f7.png

面试官:重写 equals 时为什么一定要重写 hashCode?


c7c35ac68d9fdaac0bbb8073a73ffed9.png

面试官:int和Integer有什么区别?为什么要有包装类?


ebb1486575bd7005873fbd7fed7668b9.png

面试官:final、finally、finalize 有什么区别?


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

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

相关文章

HTML 5 input placeholder 属性

<input placeholder"请先选择组织" type"text" value"" </input>placeholder 属性提供可描述输入字段预期值的提示信息&#xff08;hint&#xff09;。 该提示会在输入字段为空时显示&#xff0c;并会在字段获得焦点时消失。 注释&…

【逆强化学习-2】最大熵学习(Maximum Entropy Learning)

文章目录0.引言1.算法原理2.仿真0.引言 \qquad本文是逆强化学习系列的第2篇&#xff0c;其余博客传送门如下&#xff1a; 逆强化学习0-Introduction 逆强化学习1-学徒学习 \qquad最大熵学习是2008年出现的方法&#xff0c;原论文&#xff08;链接见【逆强化学习0】的博客&#…

uselocale_Java扫描仪useLocale()方法与示例

uselocale扫描器类useLocale()方法 (Scanner Class useLocale() method) useLocale() method is available in java.util package. useLocale()方法在java.util包中可用。 useLocale() method is used to use this Scanner locale to the given locale (lo). useLocale()方法用…

面试官又整新活,居然问我for循环用i++和++i哪个效率高?

前几天&#xff0c;一个小伙伴告诉我&#xff0c;他在面试的时候被面试官问了这么一个问题&#xff1a;在for循环中&#xff0c;到底应该用 i 还是 i &#xff1f;听到这&#xff0c;我感觉这面试官确实有点不按套路出牌了&#xff0c;放着好好的八股文不问&#xff0c;净整些幺…

UVa 988 - Many Paths, One Destination

称号&#xff1a;生命是非常多的选择。现在给你一些选择&#xff08;0~n-1&#xff09;&#xff0c;和其他选项后&#xff0c;分支数每一次选择&#xff0c;选择共求。 分析&#xff1a;dp&#xff0c;图论。假设一个状态也许是选择的数量0一个是&#xff0c;代表死亡&#xff…

Java PrintWriter close()方法与示例

PrintWriter类close()方法 (PrintWriter Class close() method) close() method is available in java.io package. close()方法在java.io包中可用。 close() method is used to close this stream and free all system resources linked with the stream. close()方法用于关闭…

pipedreader_Java PipedReader ready()方法与示例

pipedreaderPipedReader类ready()方法 (PipedReader Class ready() method) ready() method is available in java.io package. ready()方法在java.io包中可用。 ready() method is used to check whether this PipedReader stream is ready to be read or not. ready()方法用…

面试官:如何实现 List 集合去重?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;本文已收录《Java常见面试题》系列&#xff0c;开源地址&#xff1a;https://gitee.com/mydb/interviewList 去重指的…

Windows重装Anaconda3失败解决方案【重装失败10来次首次成功的案例!】

文章目录0.环境1.原因2.解决方案0.环境 Win10 Anaconda3 2018版 python 3.7.1 注意&#xff01;此种情况只会在windows上发生&#xff0c;因为在linux上你只需要删除anaconda3整个文件夹&#xff0c;重新安装一定会成功&#xff01; 1.原因 Anaconda肯定是没有成功安装的&am…

java写入文件的几种方法分享

转自&#xff1a;http://www.jb51.net/article/47062.htm 一&#xff0c;FileWritter写入文件 FileWritter, 字符流写入字符到文件。默认情况下&#xff0c;它会使用新的内容取代所有现有的内容&#xff0c;然而&#xff0c;当指定一个true &#xff08;布尔&#xff09;值作为…

python读取pcd点云/转numpy(python2+python3,非ROS环境)

0.引言 \qquadROS的PCL库支持python读取点云&#xff0c;ROS1关联的是python2&#xff08;2.7&#xff09;&#xff0c;ROS2关联的是python3&#xff08;>3.5&#xff09;&#xff0c;但这对于windows的用户和没装ROS的ubuntu用户似乎不够友好。下面就介绍两种不需要ros的方…

Java中List排序的3种方法!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在某些特殊的场景下&#xff0c;我们需要在 Java 程序中对 List 集合进行排序操作。比如从第三方接口中获取所有用户的列表&…

setdefault_Java语言环境setDefault()方法及示例

setdefault语言环境类setDefault()方法 (Locale Class setDefault() method) setDefault() method is available in java.util package. setDefault()方法在java.util包中可用。 setDefault() method is used to assign the default locale for this Locale instance of the JV…

Spring 事务失效的 8 种场景!

在日常工作中&#xff0c;如果对Spring的事务管理功能使用不当&#xff0c;则会造成Spring事务不生效的问题。而针对Spring事务不生效的问题&#xff0c;也是在跳槽面试中被问的比较频繁的一个问题。点击上方卡片关注我今天&#xff0c;我们就一起梳理下有哪些场景会导致Spring…

xcode6 AsynchronousTesting 异步任务测试

xcode集成了非常方便的测试框架&#xff0c;XCTest 在xcode6之后&#xff0c;提供了 <XCTest/XCTestCaseAsynchronousTesting.h> 利用此我们可以直接在XCTest里面测试一些异步的任务&#xff0c;比如异步网络请求 如下示例 - (void)testExample {XCTestExpectation *exce…

vscode无法识别constexpr

问题 vscode 无法识别constexpr&#xff08;常指针类型&#xff09; 方法 打开工程路径下的.vscode文件夹&#xff08;一般是自动隐藏的&#xff0c;CtrlH显示隐藏&#xff09;设置c_cpp_properties.json文件如下&#xff1a; {"configurations": [{"name…

三流Java搞技术,二流Java搞框架,一流Java…

如何反驳“99&#xff05; 的 Java 程序员都是 Spring 程序员”这句话&#xff1f;答案是不能。互联网发展至今&#xff0c;站在巨人肩膀上编程像一日三餐一样寻常。Spring Boot 的确凭一己之力拉低了 Java 开发的门槛&#xff0c;可普通开发与高开之间&#xff0c;真就因为一个…

java 方法 示例_Java语言环境getVariant()方法与示例

java 方法 示例区域设置类getVariant()方法 (Locale Class getVariant() method) getVariant() method is available in java.util package. getVariant()方法在java.util包中可用。 getVariant() method is used to get the variant code for this Locale. getVariant()方法用…

2.7-源码编译安装

网上下载源码包 wget http://网址 如果没有wget yum install -y wget建议下载下来的源码包&#xff0c;统一放到/usr/local/scr/下&#xff0c;方便维护管理养成查看INSTALL和README文档的习惯&#xff0c;内有软件安装方法和详细信息。1. ./configure --prefix/usr/l…

【Ubuntu】vscode配置PCL库/vscode无法导入PCL库

问题 PCL库是ROS框架自带的点云处理库&#xff0c;可以通过find_package(PCL REQUIRED)在CMakeLists.txt中导入&#xff0c;但是vscode却无法识别&#xff0c;出现问题如下&#xff1a; 注意&#xff0c;本文解决方案仅限Ubuntu&#xff01; 解决方案 打开工程路径下的.vsc…