ArrayList ,AbstractList和iterator在remove时会抛出异常ConcurrentModificationException

使用for循环对ArrayList在遍历的时候进行删除会有什么问题

i是累加的,但list的长度是变短的,会导致有些元素不会被遍历到,从而漏删部分元素,特别是连续相同元素

        List<String> list = new ArrayList(Arrays.asList("a", "b", "b", "c", "d"));

        //普通for循环remove调用的是fastRemove(e)
        for (int i=0;i<list.size();i++) {
            String str = list.get(i);
            if ("b".equals(str)) {
                list.remove(str);
            }
        }
        System.out.printf(list.toString());
---------------------------        
abbcd  
abcd       

使用Iterator对List集合进行删除操作时会报什么异常

会抛出异常ConcurrentModificationException

        List<String> list = new ArrayList(Arrays.asList("a", "b", "b", "c", "d"));

        //foreach实际使用的也是迭代器next()实现遍历
        for (String str : list) {
            if ("b".equals(str)) {
                list.remove(str);//iterator()
            }
        }

        //迭代器调用的是迭代器remove(),会对expectedModCount = modCount进行赋值
        Iterator<String> itr = list.iterator();
        while (itr.hasNext()) {
            String str = itr.next();
            if ("b".equals(str)) {
            	list.remove(str);//itr.remove();
            }
        }

foreach实际等价于下面的while+next循环,但是它却使用的是ArrayList.remove(e)这就意味着它的next()会出现问,在下面的源码部分会讲解。

ArrayList ,AbstractList和iterator源码解析


 AbstractList{
        protected transient int modCount = 0;//修改次数,替换,删除,排序都算修改
        listIterator(final int index)}


//ArrayList继承AbstractList
ArrayList extends AbstractList{

	  //list.remvove(e)实际调用方法
        fastRemove(e){
            modCount++//不执行checkForComodification
        }


        private class Itr implements Iterator<E>{//迭代器
			int expectedModCount = modCount;//初始值
			final void checkForComodification() {
            	if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        	}
        	
            public E next() {
                checkForComodification();//foreach()
                return (E) elementData[lastRet = i];
            }

            public void remove() {
                checkForComodification();
                try {
                    ArrayList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = modCount;//关键地方
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }

        };

        

    }
  • modCount:父类AbstractList定义的属性:修改次数,替换,删除,排序都算修改,这里主要是指定对现有数组长度、位置变化的修改次数,但是新增,update不算修改次数

  • expectedModCount :迭代器中期望的修改次数,这个值只有实现的迭代器才有,迭代器的增删改截断方法在操作时都会expectedModCount = modCount

  • checkForComodification:迭代器中的方法,每次增删改截断next()都会先做一次检查modCount != expectedModCount就会抛出异常ConcurrentModificationException

  • fastRemove:ArrayList.remove(e)实际调用的删除元素方法,它本身只做删除元素后的累加操作modCount++;并不会操作expectedModCount,因为它压根就不属于ArrayList及其父类的属性

总结

常规for遍历使用list.remove(i)实际上是使用的是 ArrayList.fastRemove

  • ArrayList.fastRemove(i),它只增加modCount++标记修改次数
  • iterator.remove(),它在删改操作时都会expectedModCount = modCount进行赋值操作
  • iterator.next()每次执行时都会checkForComodification()检查,如果modCount != expectedModCount就会抛出异常ConcurrentModificationException

因此在普通for循环中不会抛出异常,只是遍历会遗漏,从而导致部分元素漏删
iterator遍历时如果不使用iterator自有的remove()由于使用的ArrayList.remove(e)修改了modCount,而没有执行iterator中的expectedModCount = modCount,从而在next()时执行checkForComodification抛出ConcurrentModificationException

ArrayList 在remove时如何避免ConcurrentModificationException

  • ArrayList.remove(e)时不使用迭代器iterator和foreach遍历,只用for(int i=0;i

  • 使用迭代器iterator,包括foreach遍历时使用iterator.remove()

你可能感兴趣的:(java,java笔记,java,jvm,servlet)