例如:
private static void delFor() { List<String> blist = new ArrayList<>(); blist.add("a"); blist.add("b"); blist.add("c"); blist.add("d"); blist.add("e"); blist.add("f"); for (int i = 0; i < blist.size(); i++) { if(blist.get(i).equals("b")) { blist.remove(blist.get(i)); //这里输出被删除的元素有问题,主要是索引改变,删除本身没问题 System.out.println("删除的元素是: " + blist.get(i)); } } System.out.println(blist); } 复制代码
private static void delNormal() { List<String> alist = new ArrayList<>(); alist.add("1"); alist.add("2"); alist.add("3"); for (String item : alist) { if (item.equals("2")) { //删除2不报错 alist.remove(item); System.out.println("被删除的元素"+item); } } System.out.println("遍历删除后的集合" + " " + alist); } 结果:被删除的元素2 遍历删除后的集合 [1, 3] 复制代码
上面结论大家肯定知道,但是看这段代码,删除成功了,并没有报错,结论不对吗?
再看一段代码:
private static void del() { List<String> blist = new ArrayList<>(); blist.add("a"); blist.add("b"); blist.add("c"); blist.add("d"); blist.add("e"); blist.add("f"); for (String item : blist) { if(item.equals("b")) { blist.remove(item); System.out.println("删除的元素是: "+ item ); } } System.out.println("删除后的集合为:" +blist); } 复制代码
private static void delForIterator() { List<String> blist = new ArrayList<>(); blist.add("a"); blist.add("b"); blist.add("c"); blist.add("d"); blist.add("e"); blist.add("f"); Iterator it = blist.iterator(); while(it.hasNext()) { String item = (String) it.next(); if(item.equals("b")) { it.remove(); System.out.println("删除的元素是: "+ item ); } } System.out.println("删除后的集合为:" +blist); } 复制代码
private class Itr implements Iterator<E> { int cursor; // /将要访问的元素的索引 int lastRet = -1; // 上一个访问元素的索引 int expectedModCount = modCount;//expectedModCount为预期修改值,初始化等于modCount(AbstractList类中的一个成员变量) public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { //每次调用next()需要check 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]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; //重新给expectedModCount值相等 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } 复制代码
在获取一个Iterator对象时,会初始化成员变量
对于第一段代码(增强for底层还是调用迭代器),不报错是因为在删除2以后,调用hasNext()方法,cursor值移动至2,size此时变成2,相等,跳出循环,所以没有报错,这仅仅是个巧合而已。
增加for删除报错的主要原因是每次调用next()方法,都会检查expectedModCount和 ModCount值是否相等,当我们删除元素后,ModCount会改变与expectedModCount值不同,引起报错。
使用iterator删除时,看上面源码,会再次赋值它们相等,所以不会报错。
使用迭代器的iterator.remove()在单线程下是不会报错的,但是在多线程情况下,一个线程修改了集合的modCount导致另外一个线程迭代时modCount与该迭代器的expectedModCount不相等,这也会报异常。
public class RemoveListForThreads implements Runnable { static List<String> alist = new ArrayList<>(); public static void main(String[] args) { RemoveListForThreads s = new RemoveListForThreads(); alist.add("a"); alist.add("b"); alist.add("c"); alist.add("d"); BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2); ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, workQueue); for (int i = 0; i < 5; i++) { executor.execute(s); } executor.shutdown(); } @Override public synchronized void run() { Iterator<String> iterator = alist.iterator(); while (iterator.hasNext()) { String s = iterator.next(); if (s.equals("c") && Thread.currentThread().getName().equals("pool-1-thread-1")) { iterator.remove(); System.out.println(Thread.currentThread().getName() + " " + s); } System.out.println(Thread.currentThread().getName() + " " + s); } System.out.println(alist); } } 复制代码
上面这段代码创建了一个线程池,开启了五个线程,在run方法中遍历并删除“b”元素,如果该方法不加同步锁sychronized,也会抛出异常