本来开开心心写着代码,然后一运行,一堆的错误信息,瞬间心情就不好了,生产代码我这边就不贴出来了,下面老师以demo为例,给大家分享一下这个难过的历程。
public static void main(String[] args) { List<Integer> list = new LinkedList<>(); list.add(1); list.add(2); int perCount = 100, index = 0; int times = list.size() / perCount; do { List<Integer> listTemp; if (list.size() >= perCount) { listTemp = list.subList(0, perCount); } else { listTemp = list.subList(0, list.size()); } System.out.println(JSONArray.fromObject(listTemp)); list.removeAll(listTemp); index++; } while (index <= times); } Exception in thread "main" java.util.ConcurrentModificationException at java.util.SubList.checkForComodification(AbstractList.java:769) at java.util.SubList.listIterator(AbstractList.java:695) at java.util.AbstractList.listIterator(AbstractList.java:299) at java.util.SubList.iterator(AbstractList.java:691) at java.util.AbstractCollection.contains(AbstractCollection.java:99) at java.util.AbstractCollection.removeAll(AbstractCollection.java:375)上面的代码看着没啥问题,那List提供的removeAll方法为什么就报错了呢?不要着急,我们根据报错信息一步步分析。
首先我们要清楚LinkedList类的继承关系,如果是用IDEA开发的童靴,可以直接Ctrl+H查看类的继承关系。
AbstractCollection (java.util) AbstractList (java.util) AbstractSequentialList (java.util) LinkedList (java.util) KeepAliveStreamCleaner (sun.net.www.http)因为是在执行list.removeAll(listTemp)的时候报错的,list.removeAll(listTemp)方法在AbstractCollection类中,我们进入看看这个方法是如何实现的。
AbstractCollection
public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); boolean modified = false; Iterator<?> it = iterator(); while (it.hasNext()) { if (c.contains(it.next())) { it.remove(); modified = true; } } return modified; }我们可以看到这个方法的主要就是获取list集合的迭代器,如果list集合中的元素包含在listTemp集合中,就remove掉。我们看到错误栈中错误的源头是c.contains(c为AbstractCollection类对象),我们进去看看。
public boolean contains(Object o) { Iterator<E> it = iterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return true; } else { while (it.hasNext()) if (o.equals(it.next())) return true; } return false; }这个方法就更简单了,获取listTemp的迭代器,然后循环判断是否存在参数中的o元素,再对比错误栈,发现出错的地方是再 Iterator<E> it = iterator()中,iterator()方法来源于AbstractList类,我们进去这个类中看看。
AbstractList
public ListIterator<E> listIterator(final int index) { checkForComodification(); rangeCheckForAdd(index); return new ListIterator<E>() { private final ListIterator<E> i = l.listIterator(index+offset); public boolean hasNext() { return nextIndex() < size; } public E next() { if (hasNext()) return i.next(); else throw new NoSuchElementException(); } public boolean hasPrevious() { return previousIndex() >= 0; } public E previous() { if (hasPrevious()) return i.previous(); else throw new NoSuchElementException(); } public int nextIndex() { return i.nextIndex() - offset; } public int previousIndex() { return i.previousIndex() - offset; } public void remove() { i.remove(); SubList.this.modCount = l.modCount; size--; } public void set(E e) { i.set(e); } public void add(E e) { i.add(e); SubList.this.modCount = l.modCount; size++; } }; } private void checkForComodification() { if (this.modCount != l.modCount) throw new ConcurrentModificationException(); }代码很多,但是关键在于this.modCount != l.modCount这两个不相等,抛出异常导致的错误。我们首先要知道this.modCount、l.modCount分别对应着哪个类对象。this代表本身,所以就是开头的listTemp集合对象了,那l呢,跟踪l的赋值情况,我们可以得到l就是list集合对象,也就是我们需要删除元素的集合。那问题就可以转化为:为什么list的modCount和listTemp的modCount不一致呢,是哪个地方修改了它?
细心的童靴们不知道发现没,在上面有一个it.remove()方法(it就是list集合),我们进去这个方法中看看。
LinkedList
public void remove() { checkForComodification(); if (lastReturned == null) throw new IllegalStateException(); Node<E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null; expectedModCount++; } E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }罪魁祸首终于找到了,就是因为remove()方法的unlink方法导致的,最终导致两个对象的modCount大小不一致而抛出异常。
总结
问题虽然得到解决,但是modCount的作用是什么?为什么这种情况下两者的值会不一样呢?LinkedList的removeAll()会出现这种问题,但是ArrayList的removeAll()为什么不会呢?有兴趣的同学可以自己研究一下。
要更多干货、技术猛料的孩子,快点拿起手机扫码关注我,我在这里等你哦~
林老师带你学编程:https://wolzq.com
