HashMap的四种遍历方式与for each循环原理以及for each循环增删操作异常原因
首先梳理一下List、Map、Set这三种常用的集合
- List特点:元素有序,可重复
- Set特点:元素无序,不可重复
- Map特点:元素按键值对存储,无序
1.通过keySet遍历
Map<String,String> userMap = new HashMap<>(); Set<String> keySet = userMap.keySet(); for(String key : keySet){ System.out.println("key:"+key+"value:"+userMap.get(key)); }
简单好理解
2.通过Entry遍历
Map<String,String> userMap = new HashMap<>(); for(Map.Entry<String, String> m : userMap.entrySet()){ System.out.println("key:"+m.getKey()+"value:"+m.getValue()); }
Map.Entry: HashMap内部用以存储元素的对象,使用匿名内部类实现,内部维护了getKey与getValue方法
entrySet():Map类提供的方法,这个方法返回一个Map.Entry实例化后的对象集
整体性能上最为优秀的一种遍历方式,推荐使用
3.通过迭代器iterator遍历
Map<String,String> 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<String,String> userMap = new HashMap<>(); userMap.forEach((k,v) -> System.out.println("key:"+k+"value:"+v));//注意括号
API: forEach(BiConsumer<? super K,? super V> 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<String, String> 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<String> a = new ArrayList<String>(); 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<K,V> 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://www.geek-share.com/detail/2700208120.html
https://www.geek-share.com/detail/2681048143.html
https://www.geek-share.com/detail/2725539976.html
- 点赞
- 收藏
- 分享
- 文章举报
- HashMap与TreeMap的排序以及四种遍历方式
- laravel查询得到的数据(为对象object)无法进行数组循环遍历,而且不能用数组方式访问和查看的原因以及解决方法
- Java --- HashMap的四种循环遍历方式及性能对比
- 《Map中HashMap与TreeMap的排序以及四种遍历方式》
- HashMap的四种循环遍历方式
- Java HashMap循环遍历方式及其性能对比
- HashMap循环遍历方式及其性能对比
- HashMap循环遍历方式及其性能对比
- 遍历HashMap的四种方式
- 集合操作的ConcurrentModificationException异常分析,为什么有时候循环remove不会异常?Iterator方式也会异常吗?
- Map四种遍历方式以及增删改查方法总结
- Java中HashMap的四种遍历方式
- HashMap循环遍历方式及其性能对比
- Java HashMap三种循环遍历方式及其性能对比实例分析
- HashMap循环遍历方式及其性能对比
- HashMap循环遍历方式
- HashMap循环遍历方式及其性能对比
- HashMap循环遍历方式及其性能对比
- java中四种操作xml方式的比较以及四种方法的简单例子
- HashMap循环遍历方式及其性能对比