HashMap的四种遍历方式与for each循环原理以及for each循环增删操作异常原因

HashMap的四种遍历方式与for each循环原理以及for each循环增删操作异常原因

首先梳理一下List、Map、Set这三种常用的集合

List特点:元素有序,可重复Set特点:元素无序,不可重复Map特点:元素按键值对存储,无序

1.通过keySet遍历

Map userMap = new HashMap<>();

Set keySet = userMap.keySet();

for(String key : keySet){

System.out.println("key:"+key+"value:"+userMap.get(key));

}

简单好理解

2.通过Entry遍历

Map userMap = new HashMap<>();

for(Map.Entry m : userMap.entrySet()){

System.out.println("key:"+m.getKey()+"value:"+m.getValue());

}

Map.Entry: HashMap内部用以存储元素的对象,使用匿名内部类实现,内部维护了getKey与getValue方法

entrySet():Map类提供的方法,这个方法返回一个Map.Entry实例化后的对象集

整体性能上最为优秀的一种遍历方式,推荐使用

3.通过迭代器iterator遍历

Map userMap = new HashMap<>();

Iterator iter = userMap.keySet().iterator();//

while (iter.hasNext()) {

System.out.println("key:"+iter.next()+"value:"+userMap.get(iter.next()));

}

实质上即是将获取到的keySet转化为迭代器,再通过迭代器遍历,在性能上比直接用keySet稍好,同时可以在循环时操作集合内元素,这是其它遍历方法所不具备的。

4.通过Lambda表达式遍历

Map userMap = new HashMap<>();

userMap.forEach((k,v) -> System.out.println("key:"+k+"value:"+v));//注意括号

API: forEach(BiConsumer action)

这里使用了map中自带的forEach方法直接获取key与value,然后通过Lambda表达式完成输出

简洁明了,如果想装逼可以使用,性能方面并不如第二种方式

4.1:回顾一下Lambda表达式

expression = (variable) -> action

variable: 这是一个变量,一个占位符。像x,y,z,可以是多个变量;

action: 实现的代码逻辑部分,它可以是一行代码也可以是一个代码片段。

eg:int sum = (x, y) -> x + y;

5.for each循环遍历时删除元素抛出异常原理

当你尝试在for each中删除集合中一个元素时,如下

for(Map.Entry m : userMap.entrySet()){

System.out.println("key:"+m.getKey()+"value:"+m.getValue());

userMap.remove(m.getKey());

}

好像没什么问题,然而。。。

Exception in thread "main" java.util.ConcurrentModificationException

at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)

at java.util.HashMap$EntryIterator.next(HashMap.java:1479)

at java.util.HashMap$EntryIterator.next(HashMap.java:1477)

at factoryDemo.Demo.main(Demo.java:32)

使用iterator

Iterator iter = userMap.keySet().iterator();

while (iter.hasNext()) {

System.out.println("key:"+iter.next()+"value:"+userMap.get(iter.next()));

iter.remove();

}

莫得问题

为什么for each产生异常ConcurrentModificationException,而iterator不会,why?

实质上foreach循环其实就是根据集合对象创建一个iterator迭代对象,用这个迭代对象来遍历集合,相当于集合对象中元素的遍历托管给了iterator,如果要对集合进行增删操作,都必须经过iterator。

如下:一个正常的for each循环

List a = new ArrayList();

a.add("1");

a.add("2");

a.add("3");

for(String temp : a){

System.out.print(temp);

}

反编译之后:

List a = new ArrayList();

a.add("1");

a.add("2");

a.add("3");

String temp;

for(Iterator i$ = a.iterator(); i$.hasNext(); System.out.print(temp)){

temp = (String)i$.next();

}

很明显,for each在经过编译器之后,实质上为普通for循环,使用的是iterator去遍历集合,这也是为何通过iterator遍历集合的效率比直接使用KeySet效率更高。。那么问题来了,因为所有集合实现了Iterator接口,所以遍历时走的Iterator的方法,数组也可以用for each,那岂不是。。

没错:走的仍然是for(int i=0; i< len; i++)经典模式(正是在下!)咳咳,有兴趣的可以自己写一个数组遍历,然后反编译一下看看。

我们回归正题,当这里使用的iterator迭代器去遍历集合在生成iterator的时候,会保存一expectedModCount参数,这个是生成iterator的时候集合中元素的个数。如果你在遍历过程中删除元素,集合中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常。

//当我们在for each循环时删除或者增加元素时,就会使得modCount和exceptedModCount不一致,从而抛出异常,但是使用iterator.remove时为什么不出异常,查看HashMap中源代码

abstract class HashIterator :

public final void remove() {

Node p = current;

if (p == null)

throw new IllegalStateException();

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

current = null;

K key = p.key;

removeNode(hash(key), key, null, false, false);

expectedModCount = modCount;//对modCount和exceptedModCount进行了处理

}

当然不仅是HashMap,其它的集合类也会出现这种问题,所以使用迭代器循环虽然效率不是最高,但也有它的优点

引用: https://blog.csdn.net/wangjun5159/article/details/61415263 https://blog.csdn.net/bimuyulaila/article/details/52088124 https://blog.csdn.net/yueaini10000/article/details/78933289

相关推荐

365bet亚洲真人网址 渔人乐园网

渔人乐园网

📅 07-23 👁️ 483
365bet亚洲真人网址 发现一个有意思的现象:几乎所有我常用国内的App都有开屏广告,少数例外包括微信、QQ;但几乎所有我常用的海外App都没有...
365bet亚洲真人网址 文娱体育准独角兽企业「小影科技」丨杭州(准)独角兽报告