HashMap vs ConcurrentHashMap — 示例及Iterator探秘

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

如果你是一名Java开发人员,我能够确定你肯定知道ConcurrentModificationException,它是在使用迭代器遍历集合对象时修改集合对象造成的(并发修改)异常。实际上,Java的集合框架是迭代器设计模式的一个很好的实现。

Java 1.5引入了java.util.concurrent包,其中Collection类的实现允许在运行过程中修改集合对象。

ConcurrentHashMap是一个与HashMap很相似的类,但是它支持在运行时修改集合对象。

让我们通过一个简单的程序来帮助理解:

ConcurrentHashMapExample.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
packagecom.journaldev.util;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.Map;
importjava.util.concurrent.ConcurrentHashMap;
publicclassConcurrentHashMapExample {
publicstaticvoidmain(String[] args) {
//ConcurrentHashMap
Map<String,String> myMap =newConcurrentHashMap<String,String>();
myMap.put("1","1");
myMap.put("2","1");
myMap.put("3","1");
myMap.put("4","1");
myMap.put("5","1");
myMap.put("6","1");
System.out.println("ConcurrentHashMap before iterator: "+myMap);
Iterator<String> it = myMap.keySet().iterator();
while(it.hasNext()){
String key = it.next();
if(key.equals("3")) myMap.put(key+"new","new3");
}
System.out.println("ConcurrentHashMap after iterator: "+myMap);
//HashMap
myMap =newHashMap<String,String>();
myMap.put("1","1");
myMap.put("2","1");
myMap.put("3","1");
myMap.put("4","1");
myMap.put("5","1");
myMap.put("6","1");
System.out.println("HashMap before iterator: "+myMap);
Iterator<String> it1 = myMap.keySet().iterator();
while(it1.hasNext()){
String key = it1.next();
if(key.equals("3")) myMap.put(key+"new","new3");
}
System.out.println("HashMap after iterator: "+myMap);
}
}

当我们试着运行上面的程序,输出如下:

1
2
3
4
5
6
7
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
Exceptioninthread"main"java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at com.test.ConcurrentHashMapExample.main(ConcurrentHashMapExample.java:44)

查看输出,很明显ConcurrentHashMap可以支持向map中添加新元素,而HashMap则抛出了ConcurrentModificationException。

查看异常堆栈记录,可以发现是下面这条语句抛出异常:

1
String key = it1.next();

这就意味着新的元素在HashMap中已经插入了,但是在迭代器执行时出现错误。事实上,集合对象的迭代器提供快速失败(Fail-Fast)的机制,即修改集合对象结构或者元素数量都会使迭代器触发这个异常。

但是迭代器是怎么知道HashMap被修改了呢,我们可以一次取出HashMap的所有Key然后进行遍历。

HashMap包含一个修改计数器,当你调用它的next()方法来获取下一个元素时,迭代器将会用到这个计数器。

HashMap.java

1
2
3
4
5
6
7
/**
* HashMap结构的修改次数
* 结构修改是指:改变了HashMap中mapping的个数或者其中的内部结构(比如,重新计算hash值)
* 这个字段在通过Collection操作Hashmap时提供快速失败(Fail-fast)功能。
* (参见 ConcurrentModificationException)。
*/
transientvolatileintmodCount;

现在为了证明上面的观点,我们对原来的代码做一点修改,使迭代器在插入新的元素后跳出循环。只要在调用put方法后增加一个break:

1
2
3
4
if(key.equals("3")){
myMap.put(key+"new","new3");
break;
}

再执行修改后的代码,会得到下面的输出结果:

1
2
3
4
ConcurrentHashMap before iterator: {1=1, 5=1, 6=1, 3=1, 4=1, 2=1}
ConcurrentHashMap after iterator: {1=1, 3new=new3, 5=1, 6=1, 3=1, 4=1, 2=1}
HashMap before iterator: {3=1, 2=1, 1=1, 6=1, 5=1, 4=1}
HashMap after iterator: {3=1, 2=1, 1=1, 3new=new3, 6=1, 5=1, 4=1}

最后,如果我们不添加新的元素而是修改已经存在的键值对会不会抛出异常呢?

修改原来的程序并且自己验证一下:

1
2
//myMap.put(key+"new", "new3");
myMap.put(key,"new3");

如果你对于输出结果感觉困惑或者震惊,在下面评论。我会很乐意给出进一步解释。

你有没有注意到那些我们在创建集合和迭代器时的尖括号,在Java中这叫做泛型,当涉及到编译时的类型检查和去除运行时的ClassCastException的时候会很有帮助。点击这里可以了解更多泛型教程。

原文链接:  journaldev  翻译:  ImportNew.com  风恋星
译文链接:  http://www.importnew.com/8162.html
转载请保留原文出处、译者和译文链接。 ]

转载于:https://my.oschina.net/u/165124/blog/373724

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

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

相关文章

谷歌浏览器跨域报错解决办法

谷歌浏览器跨域报错&#xff1a; 在浏览器属性设置一下就可以了。 最后&#xff0c;先打开浏览器&#xff0c;就可以了。 转载于:https://www.cnblogs.com/fanyun/p/4263363.html

Unable to find the ncurses libraries or the required header files解决

问题&#xff1a; 解决方法: sudo apt-get install ncurses-dev 参考&#xff1a;Unable to find the ncurses libraries or the required header files解决 转载于:https://www.cnblogs.com/amanlikethis/p/3591353.html

iOS自动布局进阶用法

本文主要介绍几个我遇到并总结的相对高级的用法&#xff08;当然啦牛人会觉得这也不算什么&#xff09;。 简单的storyboard中上下左右约束&#xff0c;固定宽高啥的用法在这里就不做赘述了。 autolayout自动布局是iOS6以后出现的&#xff0c;但是在开始的一段时间里大家并不怎…

Windows系统与Linux系统下的硬盘分区操作

以下内容源于网络资源的学习与整理&#xff0c;如有其侵权请告知删除。 之前在uboot中利用fdisk命令对X210开发板的iNand进行分区时&#xff0c; 因为不小心在uboot中利用“fdisk -c 1”&#xff08;1对应着sd卡&#xff0c;0对应着iNand&#xff09;对sd卡也分区了&#xff…

您应该了解的 Windows Azure 网站在线工具

&#xfeff;&#xfeff;编辑人员注释&#xff1a;本文章由Windows Azure 网站团队的软件开发者 Amit Apple 撰写。 如果想要了解并亲身参与计算资源管理&#xff0c;那么您一定会很高兴得知这一消息&#xff1a;Windows Azure 网站现在提供一些非常有用的在线工具&#xff0c…

【自己给自己题目做】:如何在Canvas上实现魔方效果

最终demo -> 3d魔方 体验方法&#xff1a; 浮动鼠标找到合适的位置&#xff0c;按空格键暂停选择要翻转的3*3模块&#xff0c;找到相邻两个正方体&#xff0c;鼠标点击第一个正方体&#xff0c;并且一直保持鼠标按下的状态直到移到第二个正方体后放开&#xff0c;比如下图&…

彻底搞懂硬盘相关的概念

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 硬盘结构&#xff08;机械硬盘和固态硬盘&#xff09;详解 简单理解磁盘结构_Guanngxu的博客-CSDN博客_磁盘的结构 硬盘基础知识_Forskamse的博客-CSDN博客 硬盘知识笔记整理_落子摘星的博客-CSDN…

ubuntu下数据库的导入导出

2019独角兽企业重金招聘Python工程师标准>>> 一.导出远程数据库 例如 sudo mysqldump -h 172.16.1.211 -u haha -p123 -P3307 app>app.sql 二.导入.sql文件的 到数据库 1.create database xxx(创建一个名称是xxx的数据库) 2. use xxx(切换到该数据库下) 3. sou…

MBR分区表的简介

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考内容 &#xff08;1&#xff09;S5PV210 Uboot开发与移植01&#xff1a;Uboot概述_麦兜的学习笔记的博客-CSDN博客 &#xff08;2&#xff09;Linux系统下的硬盘分区、格式化与挂载_天糊土的博客-CSD…

Redis配置文件参数说明

配置文件参数说明: 1. Redis默认不是以守护进程的方式运行&#xff0c;可以通过该配置项修改&#xff0c;使用yes启用守护进程 daemonize no 2. 当Redis以守护进程方式运行时&#xff0c;Redis默认会把pid写入/var/run/redis.pid文件&#xff0c;可以通过pidfile指定 pidfile …

movi命令(do_movi函数的源码分析)

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 一、do_movi函数分析 当执行movi相关命令时&#xff0c;实际执行的是do_movi函数。 x210 # help movi movi init - Initialize moviNAND and show card info movi read {u-boot | kernel} {addr} - Read …

[LeetCode]Search Insert Position

原题链接&#xff1a;http://oj.leetcode.com/problems/search-insert-position/ 题意描述&#xff1a; Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You …

如何将镜像烧写至iNand(fastboot命令的源码分析)

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 u-boot sdfuse命令烧录分析----从SD卡加载内核_white_bugs的博客-CSDN博客 一、将镜像文件烧写至iNand的步骤 步骤1&#xff1a;完成准备工作。 &#xff08;1&#xff09;准备fastboot相关软件包…

你知道自己执行的是哪个jre吗?

多个JRE 我在做《Java日志工具之java.util.logging.Logger》的DEMO时&#xff0c;修改java.util.logging.Logger的配置文件&#xff0c;怎么修改都不起作用&#xff0c;因此打印了系统属性"java.home"&#xff0c;才知道自己使用的是 C:\Program Files\Java\jre7 &am…

无需写try/catch,也能正常处理异常

对于企业应用的开发者来说&#xff0c;异常处理是一件既简单又复杂的事情。说其简单&#xff0c;是因为相关的编程无外乎try/catch/finallythrow而已&#xff1b;说其复杂&#xff0c;是因为我们往往很难按照我们真正需要的策略来处理异常。我一直有这样的想法&#xff0c;理想…

vs2010插件

转载于:https://www.cnblogs.com/tinytiny/p/3608030.html

leetcode1——两数之和

一、提出问题 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按…

如何脱离SDK,使用DW5.5和phonegap以及JQMobile搭建开发环境

也许有些人是学C出身&#xff0c;对于Java几乎不了解。一时心血来潮想学学android开发&#xff0c;于是下载了Eclipse&#xff0c;安装了SDK&#xff0c;有模有样的学习起来。也许是懒惰了&#xff0c;对于java一直总是提不起精神。于是确定使用DreamweaverJquery mobilePhoneg…

leetcode2——两数相加

一、提出问题 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式存储的&#xff0c;并且每个节点只能存储一位数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以…

32为Linux安卓AVD启动报错

2019独角兽企业重金招聘Python工程师标准>>> 当使用android的AVD时提示以下错误&#xff1a;Starting emulator for AVD NexusOne ERROR: 32-bit Linux Android emulator binaries are DEPRECATED, to use them you will have to do at least one of the following:…