list删除操作 java.util.ConcurrentModificationException
首先大家先看一段代码:
public static void main(String[] args) {
List<String> listStr = new ArrayList<String>();
listStr.add("1");
listStr.add("2");
listStr.add("3");
listStr.add("4");
listStr.add("5");
System.out.println("listStr.size:::"+listStr.size());
for(String str:listStr){
if("3".equals(str)){
listStr.remove(str);
}
}
System.out.println("listStr.size:::"+listStr.size());
}
现象:
该程序会抛出一个 java.util.ConcurrentModificationException异常。
分析:
对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,会抛出java.util.ConcurrentModificationException 异常。 因为在修改数据时不能同步原有list中数据,当下一次循环时找不到数据。
解决办法:
Iterator 在工作的时候是不允许被迭代的对象被改变的,使用 Iterator 本身的方法 remove() 来删除对
象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
改后代码:
public static void main(String[] args) {
List<String> listStr = new ArrayList<String>();
listStr.add("1");
listStr.add("2");
listStr.add("3");
listStr.add("4");
listStr.add("5");
System.out.println("listStr.size:::"+listStr.size());
Iterator<String> ite = listStr.iterator();
while(ite.hasNext()){
String str = (String)ite.next();
if("3".equals(str)){
ite.remove();
}
}
System.out.println("listStr.size:::"+listStr.size());
}
补充:
或许有人可能会问如果我不用增强for循环,直接用.get(index)方法会不会报错?
代码如下:
for(int i = 0 ; i < listStr.size(); i++){
if("3".equals(listStr.get(i))){
listStr.remove(i);
}
}
现象:
listStr.size:::5
listStr.size:::4
大家可以看到通过list的下表索引来修改list数据是不会出错的。
分析:
大家可以这样认为:通过所引来循环时,jvm记录的是所引值,当移除当前对象后,其他元素的索引号不会同步改变。下次循环仍可以找到对应数据。而增强for循环,记录的是当前对象,当下次循环时,会先找到该对象,然后游标向下移,这时候找不到对象所以结果会混乱。
java.util.ConcurrentModificationException
【引言】
经常在迭代集合元素时,会想对集合做修改(add/remove)操作,类似下面这段代码:
- for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
- Integer val = it.next();
- if (val == 5) {
- list.remove(val);
- }
- }
运行这段代码,会抛出异常java.util.ConcurrentModificationException。
【解惑】
(以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看AbsrtactList中iteraor方法
- public Iterator<E> iterator() {
- return new Itr();
- }
它返回一个内部类,这个类实现了iterator接口,代码如下:
- private class Itr implements Iterator<E> {
- int cursor = 0;
- int lastRet = -1;
- int expectedModCount = modCount;
- public boolean hasNext() {
- return cursor != size();
- }
- public E next() {
- checkForComodification();
- try {
- E next = get(cursor);
- lastRet = cursor++;
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public void remove() {
- if (lastRet == -1)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- // 修改expectedModCount 的值
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
- final void checkForComodification() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- }
- }
在内部类Itr中,有一个字段expectedModCount ,初始化时等于modCount,即当我们调用list.iterator()返回迭代器时,该字段被初始化为等于modCount。在类Itr中next/remove方法都有调用checkForComodification()方法,在该方法中检测modCount == expectedModCount,如果不相当则抛出异常ConcurrentModificationException。
前面说过,在集合的修改操作(add/remove)中,都对modCount进行了+1。
在看看刚开始提出的那段代码,在迭代过程中,执行list.remove(val),使得modCount+1,当下一次循环时,执行 it.next(),checkForComodification方法发现modCount != expectedModCount,则抛出异常。
【解决办法】
如果想要在迭代的过程中,执行删除元素操作怎么办?
再来看看内部类Itr的remove()方法,在删除元素后,有这么一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代时,删除元素,可以使用迭代器提供的remove方法。对于add操作,则在整个迭代器迭代过程中是不允许的。 其他集合(Map/Set)使用迭代器迭代也是一样。
//获奖显示 public String prizeList(){ voteList = userWorksService.getAllRank(); UserWorks userWorks = new UserWorks(); Iterator<UserWorks> list = voteList.iterator(); Integer rank ; while(list.hasNext()){ userWorks = list.next(); if(!("9".equals(userWorks.getAwardsRank()))){ rank = Integer.parseInt(userWorks.getAwardsRank())+2; userWorks.setAwardsRank(rank +""); allList.add(userWorks); } } voteList.removeAll(allList); return "prizeList"; }
相关文章
- java中数组转列表_Java数组转list
- java数组和list转换_js将数组转换成字符串
- 二级java程序设计--Java SDK6.0的下载和操作[通俗易懂]
- java list去重_JAVA基础-List去重的6种方式[通俗易懂]
- java开发常用四大框架_大牛经验!常用的5款Java框架汇总[通俗易懂]
- java public interface_Java 接口interface的基础[通俗易懂]
- java后台怎么解密md5,Java md5 密码加解密
- java dom4j生成xml格式化_Java DOM4J方式生成XML的方法「建议收藏」
- JAVA string转map_java怎么转业务
- java 自定义的类加载器_Java如何自定义类加载器[通俗易懂]
- BigDecimal.setScale 处理java小数点
- java list 转json 字符串_JSON的String字符串与Java的List列表对象的相互转换
- java list 转json 字符串_Java之JSON字符串与List集合之间相互转换
- java h2 数据库_Java H2数据库
- 从java到JavaScript(2):对比Java/Go/Swift/Rust看Dart
- Java基础——List、Set、Map的简单操作与遍历
- Java中对List集合的常用操作详解编程语言
- java中对List中对象排序实现详解编程语言
- Java操作MySQL数据库快速入门(java访问mysql)
- 服务Linux启动Java服务:一步步去完成(linux启动java)
- Java调试Linux:探索新的技术可能性(java调试Linux)
- Java编程操作Oracle数据库(java操作oracle)
- 服务器上的文件Java获取Linux服务器文件:简单又高效的操作方法(java获取linux)
- Java高效操作MySQL数据库(java写入mysql)
- 监控使用Java开发Redis过期监控系统(redisjava过期)
- Linux下安装Java 开发环境指南(linux装java环境)
- 结构使用Redis List结构实现快速操作(redis中list)
- Java程序员的MySQL数据库之旅(java操作mysql数据库)
- 使用Java程序执行Linux指令:实现自动化操作(java执行linux命令)
- 缓存使用Redis让Java代码更加迅速缓存设置(redis设置java)
- java操作mongodb基础(查询排序输出list)