博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java 集合类如何正确在迭代中添加删除元素
阅读量:2429 次
发布时间:2019-05-10

本文共 2936 字,大约阅读时间需要 9 分钟。

刚开始尝试写博客不久,今天开创一个新的内容方向java,这是我的第一篇关于java的博客。

最近在完成java作业时,遇到了一个使用迭代器的问题。
问题还原
遇到问题的代码过于复杂,我这里写了一个非常简单的反例,足以还原问题的原貌。

import java.util.*;public class test{    public static void main(String[] args){        List
number = new LinkedList<>(); number.addAll(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)); for (Integer i : number) { if (i > 5) number.remove(i); } System.out.println(number.toString()); }}

这段代码目的是去掉ArrayList 类型的: number中大于5的数字

报错结果是这样的

Exception in thread "main" java.util.ConcurrentModificationException        at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)        at java.util.LinkedList$ListItr.next(LinkedList.java:888)        at test.main(test.java:6)

出现了这个 java.util.ConcurrentModificationException的异常。

原因分析:

上面代码中我使用了Iterator迭代器,从而可以使用foreach语法实现遍历。Iterator是一个接口,Itr是一个实现这个接口的类,为了探究原因,最好查看一下迭代器的源码,在IDEA下,按住ctrl点击Itr,就看到了源码。
注意到在源码中,Itr的开头有一句

int expectedModCount = modCount;

也就是在类创建时,就将modCount赋值给了expectedModCount

然后在迭代器开始迭代,即调用next()这个函数的时候,都会调用一次内部的checkForComodification()函数
这个函数我摘录出来,如下:

final void checkForComodification() {    if (modCount != expectedModCount)        throw new ConcurrentModificationException();}

这里就得到了ConcurrentModificationException这个异常的来源,是因为modCount和expectedModCount不相等。

modCount是原来集合的版本号,每次修改(增、删)集合都会加1;expectedModCount是当前集合的版本号,当我在迭代过程中,删除了一个元素,原来集合的modCount就+1,而Itr迭代器实例是在迭代开始时创立的,expectedModCount并不会随着迭代过程发生改变,所以就会出现两个值不想等的情况。
除此之外,java这样检查的原因在于一个叫快速失败机制(fail-fast)
快速失败机制产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过Iterator遍历集合时,该集合的内容被其他线程所改变,则会抛出ConcurrentModificationException异常。
所以java这样检查为的是多线程数据处理的安全性。

解决问题

由于在外部LinkedList本身删除或添加一个元素,在迭代器内部的expectedModCount没有跟着变化,所以就要使用迭代器自己提供的remove和add函数,这样迭代的expectedModCount就会同步变化,就不会报错。
修改后的代码如下:

import java.util.*;public class test{    public static void main(String[] args){        List
number = new LinkedList<>(); number.addAll(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)); Iterator
iterator = number.iterator(); while(iterator.hasNext()) { // 判断是否还有下一个元素 int i = iterator.next(); if (i > 5) // 注意这里使用的是迭代器对象 iterator的remove,不是number的remove iterator.remove(); } System.out.println(number.toString()); }}

同时,在java8中加入了函数式编程的一些语法,所以我用lamada表达式配合removeif函数来实现以上功能时,代码量会大大减小,这里一起分享一下:

import java.util.*;public class test{    public static void main(String[] args){        List
number = new LinkedList<>(); number.addAll(Arrays.asList(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)); number.removeIf(num->num>5); // 这里的遍历和删除只有一短行就实现了 System.out.println(number.toString()); }}

具体的lamda表达式生动解释,参见。

补充一点:上面的例子之所以用LinkedList,而不用List接口的另一个实现ArrayList类,是因为ArrayList的内部实现是依赖于数组的,数组本身不方便插入和删除,所以ArrayList类在实现接口函数remove时,直接抛出UnsupportedOperationException异常,也就是它不支持插入和删除,相比之下,LinkedList类是基于链表数据结构的实现,对于插入和删除就非常的简单和高效了。

转载地址:http://lijmb.baihongyu.com/

你可能感兴趣的文章
如何用 Python 一键查房价?
查看>>
累死累活干不过写 PPT 的?新东方年会吐槽视频俞敏洪不罚反奖 12 万!
查看>>
“离开360时,它只给了我一块钱”
查看>>
PDF 翻译神器,再也不担心读不懂英文 Paper 了
查看>>
漫话:如何给女朋友解释什么是RPC
查看>>
@程序员,为什么别人比你优秀?!
查看>>
你的红包狂欢夜,互联网巨头们的流量争夺战
查看>>
春晚过去 4 天了,你卸载百度 APP 了吗?
查看>>
中国移动互联网十年
查看>>
面试官问:请拿出一段体现你水平的代码,我该如何回答?
查看>>
@程序员,沟通这项核心技能你掌握了多少?
查看>>
2019,九问联想贺志强
查看>>
你可以忍受大城市 365 天的孤独,却不能忍受小城市 7 天的热闹
查看>>
35 岁程序员,年后第一天被辞退
查看>>
情人节她说:是的,嫁人当嫁程序员
查看>>
骚操作!代码写情诗 | 程序员有话说
查看>>
小程序卡卡卡?用这个方法后,渲染速度提升三倍!
查看>>
二线城市容不下程序员
查看>>
不要成为自己讨厌的那种程序员 | 程序员有话说
查看>>
为什么程序员下班后只关显示器从不关电脑?
查看>>