java.util.ConcurrentModificationException异常;java.util.ConcurrentModificationException实战

 

 

写代码遇到这个问题,很多博客文章都是在反复的强调理论,而没有对应的实例,所以这里从实例出发,后研究理论:

一、错误产生情况

1 、字符型

   (1)添加

 public static void main(String[] args) {

        List<String> stringList = new ArrayList<String>();
        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         List<String> stringList = new ArrayList<String>();
 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<String> it = stringList.iterator();
15                 while (it.hasNext()) {
16                       it.next();
17                 }
18             }
19         }
20 
21         System.out.println(stringList);
22 
23     }
View Code

打印出结果:

   

然后我们打印其next,就会发现其循环就是通过it.next()方法将数据添加进去的

打印:

  (2)、删除

错误写法:

 1   private static String key = "钱二";
 2 
 3 
 4     public static void main(String[] args) {
 5 
 6         List<String> stringList = new ArrayList<String>();
 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     }
View Code

报错:

改写为:

 1  private static String key = "钱二";
 2 
 3 
 4     public static void main(String[] args) {
 5 
 6         List<String> stringList = new ArrayList<String>();
 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<String> 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     }
View Code

结果:

      2、整形

  正确添加:

 1 public class ConcurrentBaseApplication {
 2 
 3     private static String key = "钱二";
 4 
 5 
 6     public static void main(String[] args) {
 7 
 8         List<Integer> integerList = new ArrayList<Integer>();
 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<Integer> 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 }
View Code

结果:

正确删除:

 1 public class ConcurrentBaseApplication {
 2 
 3     public static void main(String[] args) {
 4 
 5         List<Integer> integerList = new ArrayList<Integer>();
 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<Integer> 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 }
View Code

 结果:

 (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         List<Student> students = new ArrayList<Student>();
 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<Student> 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 }
View Code

结果:

  正确的删除:

 1 public class ConcurrentBaseApplication {
 2 
 3     public static void main(String[] args) {
 4 
 5         List<Student> students = new ArrayList<Student>();
 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<Student> 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 }
View Code

结果:

结果集:

1 [Student{name='李雷', age='13', id='617e914f-ed33-472d-bbbd-1a6bf5ef5901'}, Student{name='韩梅梅', age='14', id='cb804e43-4846-4fc6-84c8-9e4a6b17d7f1'}]
View Code

 二、原理补充

继承关系图

 

根据错误我们知道是Itr出错了

 

根据添加class,可以看到它跟ArrayList的关系

Itr是一个内部类,还实现了Iterator

其源码

 1 /**
 2      * An optimized version of AbstractList.Itr
 3      */
 4     private class Itr implements Iterator<E> {
 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     }
View Code

想要调用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异常。

 

posted @ 2019-07-22 00:47  北极的大企鹅  阅读(417)  评论(0编辑  收藏  举报
阅读 - 79万