错误的写法
- 不会抛异常,但是会漏掉数据
List<Integer> list = new ArrayList<>();
list.add()...
for(int i = 0;i < list.size();i++) {
if(i == xxx) {
list.remove(i);
}
}
该种写法每一次随着条件的成立,会带来如下问题
- 每一次remove掉一个元素,导致数组后面所有的所有元素向前移位,之后索引本身自增,结果就是reomve后面的那个元素会被漏掉检查
- 快速抛异常
List<Integer> list = new ArrayList<>();
list.add()...
while (iterator.hasNext()) {
list.remove(iterator.next());
}
该种方法会快速的抛出异常,相同的写法如下:
List<Integer> list = new ArrayList<>();
list.add()...
for(Integer integer : list) {
list.remove(integer);
}
之所以写法相同是因为forEach循环经过JVM解析之后的指令同Iterator一样,可见第二种写法仅仅是Java提供的一种语法糖。快速抛出异常的原因参见ArrayList源码,部分源码如下:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
每次remove()操作都会修改modCount的值,但是每一调用iterator.next()方法会检查exceptedModCount==modCount, 如果不相等就会抛出ConcurrentModificationException, 部分源码如下:
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
正确的写法
- 巧妙的写法
for(int i = list.size()-1;i >= 0;i--) {
list.remove(i);
}
从后向前遍历,每次remove操作之后,后面的元素仍然向前移动,但是前面元素的顺序不便,因此索引自增之后指向的仍然原来应该指向的元素。
- 官方推荐的写法
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
iterator.remove()方法不同于ArrayList.remove()方法,源码如下:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
每次remove()操作都会将modCount同步到exceptedModCount值中,这样就可以安全的删除数组中的元素。
官方的话
* The iterators returned by this class's {@link #iterator() iterator} and
* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
* if the list is structurally modified at any time after the iterator is
* created, in any way except through the iterator's own
* {@link ListIterator#remove() remove} or
* {@link ListIterator#add(Object) add} methods, the iterator will throw a
* {@link ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
网友评论