错误做法
一、fori正序 + list.remove(num)
@Test
public void test031(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(3);
for (int i = 0; i < list.size(); i++) {
Integer num=list.get(i);
if(num==3){
list.remove(num);
}
}
list.forEach(System.out::println);// 1 3
}
我们可以看到,集合中有两个3,结果只删掉了第一个,原因是删除第一个3之后,第二个3的下标变成1,缺少了一次检查
改成方法:删除之后i–,回退一次,见下面的正确做法一
二、增强for + list.remove(num)
@Test
public void test07(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
for (Integer num:list) {
if(num==1){
list.remove(num);
}
}
list.forEach(System.out::println);
}
我们看到报错的是ArrayList内部迭代器的next方法中的checkForCommdification方法抛出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();
}
我们可以看到modCount != expectedModCount,而抛出异常
原因是remove的时候修改了modCount,但是并未修改expectedModeCount
ArrayList的remove(Object o)的源码如下:
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 remove method that skips bounds checking and does not * return the value removed. */
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
}
修正方法:使用iteritor的remove(),见下面正确方法的三
正确做法
一、fori正序 + list.remove(num) + 回退
@Test
public void test03(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(3);
for (int i = 0; i < list.size(); i++) {
Integer num=list.get(i);
if(num==3){
list.remove(num);
i--//注意回退,不然会缺少一次检查
}
}
list.forEach(System.out::println);// 1 3
}
二、fori倒序 + list.remove(num)
@Test
public void test04(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
for (int i = list.size()-1; i >=0; i--) {
Integer num=list.get(i);
if(num==1){
list.remove(num);
}
}
list.forEach(System.out::println);
}
倒叙删除不用回退,如下图
三、iterator.remove()
@Test
public void test01(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if(next==3){
iterator.remove();
}
}
list.forEach(System.out::println);
}
这种做法不会抛出ConcurrentModificationException的原因是,iterator.remove()也调用了remove(Object o),但是它把modCount的值,也赋值给了exceptionCount
源码如下:
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();
}
}
四、list.removeIf(条件)
@Test
public void test02(){
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(3);
list.removeIf(next -> next == 3);
list.forEach(System.out::println);
}
注意事项
- remove有两个重载方法,一个接收int的index,一个接收Object的o,注意不要写错