腾讯一面!说说ArrayList的遍历foreach与iterator时remove的区别,我一脸懵逼 (3)

删除指定位置处的元素:

/** * ArrayList: */ public E remove(int index) { //index参数校验 rangeCheck(index); //修改次数+1 modCount++; //获取指定index位置处的元素 E oldValue = elementData(index); //numMoved记录的是移动元素的个数 int numMoved = size - index - 1; if (numMoved > 0) /* 同上面fastRemove方法中的解释,这里同样是将index+1位置处以及后面的数组元素往前移动一位, 这会将index位置处的元素被覆盖,也就是做了删除(这里是否可以考虑封装?) */ System.arraycopy(elementData, index + 1, elementData, index, numMoved); //同上,将最后一个位置(--size)置为null elementData[--size] = null; //删除之后,将旧值返回就行了 return oldValue; } 6 不要在foreach循环里进行元素的remove/add操作

这是《阿里巴巴编码规范》中的一条。正例:

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String item = iterator.next(); if ("2".equals(item)) { iterator.remove(); } }

反例:

List<String> list = new ArrayList<>(); list.add("1"); list.add("2"); for (String item : list) { if ("2".equals(item)) { list.remove(item); } }

​ 运行上面的代码可以看到,使用迭代器的删除操作是不会有问题、能成功删除的;而使用foreach循环进行删除则会抛出ConcurrentModificationException异常,但如果使用foreach循环删除第一个元素“1”的时候又会发现不会抛出异常。那么这到底是为什么呢?

​ 众所周知,foreach循环是一种语法糖,那么其背后到底是如何来实现的呢?将上面反例的代码反编译后的结果如下:

public class com.hys.util.Test { public com.hys.util.Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: ldc #4 // String 1 11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 16: pop 17: aload_1 18: ldc #6 // String 2 20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 25: pop 26: aload_1 27: invokeinterface #7, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 32: astore_2 33: aload_2 34: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 39: ifeq 72 42: aload_2 43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 48: checkcast #10 // class java/lang/String 51: astore_3 52: ldc #6 // String 2 54: aload_3 55: invokevirtual #11 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 58: ifeq 69 61: aload_1 62: aload_3 63: invokeinterface #12, 2 // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z 68: pop 69: goto 33 72: return }

​ 上面的内容不需要完全看懂,只需要看到第23行代码处、第26行代码处和第29行代码处后面的解释,也就是foreach循环是通过调用List.iterator方法来生成一个迭代器,通过Iterator.hasNext方法和Iterator.next方法来实现的遍历操作(普通的for循环不是通过这种方式,也就是说普通的for循环不会有这种问题)。

​ 那么首先来看一下ArrayList中iterator方法的实现:

public Iterator<E> iterator() { return new Itr(); } 可以看到是返回了一个内部类Itr: /** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } //... }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzyf.html