java.util.ConcurrentModificationException异常;java.util.ConcurrentModificationException实战
写代码遇到这个问题,很多博客文章都是在反复的强调理论,而没有对应的实例,所以这里从实例出发,后研究理论:
一、错误产生情况
1 、字符型
(1)添加
public static void main(String[] args) { ListstringList = new ArrayList (); stringList.add("张三毛"); stringList.add("李四"); stringList.add("王五"); stringList.add("钱二"); if (stringList!=null) { if (!stringList.equals(Collections.EMPTY_LIST.isEmpty())){ for (String s : stringList) { stringList.add("赵大"); } } } System.out.println(stringList); }
报错
改写为如下即可:
1 public static void main(String[] args) { 2 3 ListstringList = new ArrayList (); 4 stringList.add("张三毛"); 5 stringList.add("李四"); 6 stringList.add("王五"); 7 stringList.add("钱二"); 8 9 if (stringList != null) { 10 if (!stringList.equals(Collections.EMPTY_LIST.isEmpty())) { 11 12 stringList.add("赵大"); 13 14 Iterator it = stringList.iterator(); 15 while (it.hasNext()) { 16 it.next(); 17 } 18 } 19 } 20 21 System.out.println(stringList); 22 23 }
打印出结果:
然后我们打印其next,就会发现其循环就是通过it.next()方法将数据添加进去的
打印:
(2)、删除
错误写法:
1 private static String key = "钱二"; 2 3 4 public static void main(String[] args) { 5 6 ListstringList = new ArrayList (); 7 stringList.add("张三毛"); 8 stringList.add("李四"); 9 stringList.add("王五"); 10 stringList.add("钱二"); 11 12 if (stringList != null) { 13 if (!stringList.equals(Collections.EMPTY_LIST.isEmpty())) { 14 15 for (String s : stringList) { 16 if (key.equals(s)){ 17 stringList.remove(s); 18 } 19 } 20 } 21 } 22 23 System.out.println(stringList); 24 25 }
报错:
改写为:
1 private static String key = "钱二"; 2 3 4 public static void main(String[] args) { 5 6 ListstringList = new ArrayList (); 7 stringList.add("张三毛"); 8 stringList.add("李四"); 9 stringList.add("王五"); 10 stringList.add("钱二"); 11 12 if (stringList != null) { 13 if (!stringList.equals(Collections.EMPTY_LIST.isEmpty())) { 14 Iterator it = stringList.iterator(); 15 16 while (it.hasNext()) { 17 String next = it.next(); 18 19 if (key.equals(next)) { 20 it.remove(); 21 } 22 23 } 24 25 } 26 } 27 28 System.out.println(stringList); 29 30 }
结果:
2、整形
正确添加:
1 public class ConcurrentBaseApplication { 2 3 private static String key = "钱二"; 4 5 6 public static void main(String[] args) { 7 8 ListintegerList = new ArrayList (); 9 integerList.add(1); 10 integerList.add(2); 11 integerList.add(3); 12 integerList.add(4); 13 14 Integer next=0; 15 16 if (integerList != null) { 17 if (!integerList.equals(Collections.EMPTY_LIST.isEmpty())) { 18 integerList.add(5); 19 20 Iterator it = integerList.iterator(); 21 22 while (it.hasNext()) { 23 next = it.next(); 24 System.out.println(next); 25 } 26 27 } 28 } 29 30 System.out.println(integerList); 31 32 } 33 34 }
结果:
正确删除:
1 public class ConcurrentBaseApplication { 2 3 public static void main(String[] args) { 4 5 ListintegerList = new ArrayList (); 6 integerList.add(1); 7 integerList.add(2); 8 integerList.add(3); 9 integerList.add(4); 10 11 if (integerList != null) { 12 if (!integerList.equals(Collections.EMPTY_LIST.isEmpty())) { 13 14 Iterator it = integerList.iterator(); 15 16 while (it.hasNext()) { 17 Integer next = it.next(); 18 if("2".equals(next.toString())){ 19 it.remove(); 20 } 21 } 22 } 23 } 24 25 System.out.println(integerList); 26 27 } 28 29 }
结果:
(3) 实体类
创建实体类
Student
package com.north.big.penguin.pojo; import java.io.Serializable; /** * @author liuyangos8888 */ public class Student implements Serializable { /** * 姓名 */ private String name; /** * 年龄 */ private String age; /** * 标识 */ private String id; public Student() { } public Student(String name, String age, String id) { this.name = name; this.age = age; this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age='" + age + '\'' + ", id='" + id + '\'' + '}'; } }
正确的添加:
1 public class ConcurrentBaseApplication { 2 3 public static void main(String[] args) { 4 5 Liststudents = new ArrayList (); 6 7 Student student1 = new Student(); 8 student1.setName("李雷"); 9 student1.setAge("13"); 10 student1.setId(UUID.randomUUID().toString()); 11 12 Student student2 = new Student(); 13 student2.setName("韩梅梅"); 14 student2.setAge("14"); 15 student2.setId(UUID.randomUUID().toString()); 16 17 Student student3 = new Student(); 18 student3.setName("李华"); 19 student3.setAge("15"); 20 student3.setId(UUID.randomUUID().toString()); 21 22 23 students.add(student1); 24 students.add(student2); 25 students.add(student3); 26 27 if (students != null) { 28 if (!students.equals(Collections.EMPTY_LIST.isEmpty())) { 29 30 Student student4 = new Student(); 31 student4.setName("小明"); 32 student4.setAge("16"); 33 student4.setId(UUID.randomUUID().toString()); 34 35 36 Iterator it = students.iterator(); 37 38 while (it.hasNext()) { 39 // 添加学生 40 Student next = it.next(); 41 } 42 } 43 } 44 45 System.out.println(students); 46 47 } 48 49 }
结果:
正确的删除:
1 public class ConcurrentBaseApplication { 2 3 public static void main(String[] args) { 4 5 Liststudents = new ArrayList (); 6 7 Student student1 = new Student(); 8 student1.setName("李雷"); 9 student1.setAge("13"); 10 student1.setId(UUID.randomUUID().toString()); 11 12 Student student2 = new Student(); 13 student2.setName("韩梅梅"); 14 student2.setAge("14"); 15 student2.setId(UUID.randomUUID().toString()); 16 17 Student student3 = new Student(); 18 student3.setName("李华"); 19 student3.setAge("15"); 20 student3.setId(UUID.randomUUID().toString()); 21 22 23 students.add(student1); 24 students.add(student2); 25 students.add(student3); 26 27 if (students != null) { 28 if (!students.equals(Collections.EMPTY_LIST.isEmpty())) { 29 30 Student student4 = new Student(); 31 student4.setName("小明"); 32 student4.setAge("16"); 33 student4.setId(UUID.randomUUID().toString()); 34 35 36 Iterator it = students.iterator(); 37 38 while (it.hasNext()) { 39 // 添加学生 40 Student next = it.next(); 41 42 Integer integerAge = Integer.valueOf(next.getAge()); 43 44 if(integerAge>14){ 45 it.remove(); 46 } 47 } 48 } 49 } 50 51 System.out.println(students); 52 53 } 54 55 }
结果:
结果集:
1 [Student{name='李雷', age='13', id='617e914f-ed33-472d-bbbd-1a6bf5ef5901'}, Student{name='韩梅梅', age='14', id='cb804e43-4846-4fc6-84c8-9e4a6b17d7f1'}]
二、原理补充
继承关系图
根据错误我们知道是Itr出错了
根据添加class,可以看到它跟ArrayList的关系
Itr是一个内部类,还实现了Iterator
其源码
1 /** 2 * An optimized version of AbstractList.Itr 3 */ 4 private class Itr implements Iterator{ 5 int cursor; // index of next element to return 6 int lastRet = -1; // index of last element returned; -1 if no such 7 int expectedModCount = modCount; 8 9 Itr() {} 10 11 public boolean hasNext() { 12 return cursor != size; 13 } 14 15 @SuppressWarnings("unchecked") 16 public E next() { 17 checkForComodification(); 18 int i = cursor; 19 if (i >= size) 20 throw new NoSuchElementException(); 21 Object[] elementData = ArrayList.this.elementData; 22 if (i >= elementData.length) 23 throw new ConcurrentModificationException(); 24 cursor = i + 1; 25 return (E) elementData[lastRet = i]; 26 } 27 28 public void remove() { 29 if (lastRet < 0) 30 throw new IllegalStateException(); 31 checkForComodification(); 32 33 try { 34 ArrayList.this.remove(lastRet); 35 cursor = lastRet; 36 lastRet = -1; 37 expectedModCount = modCount; 38 } catch (IndexOutOfBoundsException ex) { 39 throw new ConcurrentModificationException(); 40 } 41 } 42 43 @Override 44 @SuppressWarnings("unchecked") 45 public void forEachRemaining(Consumer<? super E> consumer) { 46 Objects.requireNonNull(consumer); 47 final int size = ArrayList.this.size; 48 int i = cursor; 49 if (i >= size) { 50 return; 51 } 52 final Object[] elementData = ArrayList.this.elementData; 53 if (i >= elementData.length) { 54 throw new ConcurrentModificationException(); 55 } 56 while (i != size && modCount == expectedModCount) { 57 consumer.accept((E) elementData[i++]); 58 } 59 // update once at end of iteration to reduce heap write traffic 60 cursor = i; 61 lastRet = i - 1; 62 checkForComodification(); 63 } 64 65 final void checkForComodification() { 66 if (modCount != expectedModCount) 67 throw new ConcurrentModificationException(); 68 } 69 }
想要调用iteror转化ArrayList就要找iterator方法
Itr参数解析
参数 | 含义 |
cursor | 一个索引,代表下一个要访问的元素的索引 |
astRet | 表示上一个访问的元素的索引 |
expectedModCount | 表示修改次数的 |
modCount | 表示对List的修改次数 |
其方法主要是hasNext()和next()两个方法,
用其判断是否还有元素未被访问,代码中
while(iter.hasNext()){
如果下一个访问的元素下标不等于ArrayList的大小,就表示有元素需要访问,这个很容易理解,如果下一个访问元素的下标等于ArrayList的大小,则肯定到达末尾了。
首先在next()方法中会调用checkForComodification()方法,然后根据cursor的值获取到元素,接着将cursor的值赋给lastRet,并对cursor的值进行加1操作。初始时,cursor为0,lastRet为-1,那么调用一次之后,cursor的值为1,lastRet的值为0。注意此时,modCount为0,expectedModCount也为0。
当判断当前元素的值是否为2,若为2,则调用list.remove()方法来删除该元素。
ArrayList中的remove
通过remove方法删除元素最终是调用的fastRemove()方法,在fastRemove()方法中,首先对modCount进行加1操作(因为对集合修改了一次),然后接下来就是删除元素的操作,最后将size进行减1操作,并将引用置为null以方便垃圾收集器进行回收工作。
执行完删除操作后,继续while循环,调用hasNext方法()判断,由于此时cursor为1,而size为0,那么返回true,所以继续执行while循环,然后继续调用iterator的next()方法.
如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常。
此时modCount为1,而expectedModCount为0,因此程序就抛出了ConcurrentModificationException异常。