1、for循环方式删除元素
public class Main
{
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
System.out.println("删除前的list:" + list.toString());
for (int i = 0, len = list.size(); i < len; i++) {
if ("c".equals(list.get(i))) {
list.remove(i);
}
}
System.out.println("删除后的list" + list.toString());
}
}
执行结果:
删除前的list:[a, b, c, d, e]
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 4, Size: 4
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at Main.main(Main.java:17)
remove删除后,会将后续元素前移一位,并且size减去1。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
get的时候,首先调用rangeCheck()验证索引是否越界。
上面案例中len设为了最初的长度5,后面删除掉元素后仍是按照5的长度来遍历,导致越界
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
private void rangeCheck(int index) {
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
可以将遍历size改成动态 for (int i = 0; i < list.size(); i++)
2、增强for循环方式删除元素
public class Main
{
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
System.out.println("删除前的list:" + list.toString());
for (String s : list) {
if ("c".equals(s)) {
list.remove(s);
}
}
System.out.println("删除后的list" + list.toString());
}
}
运行输出:
删除前的list:[a, b, c, d, e, f]
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at Main.main(Main.java:16)
增强循环本质上是迭代器循环,删除时用的元素内容匹配删除,其中会执行fastRemove()来真正删除,fastRemove()中会对modCount加1,modCount代表对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
}
当后面再次取值的时候,先调用hasNext()判断是否已经取到最后一个值。
然后调用next()取值,next()会调用 checkForComodification()进行验证,发现modCount和expectedModCount不相等,抛出异常。
expectedModCount,是ArrayList\(Itr中的成员变量,表示对ArrayList修改次数的期望值,在循环开始的时候初始化值为modCount。ArrayList.remove()不会对expectedModCount造成变动,ArrayList\)Itr.remove()才会。
public boolean hasNext() {
return cursor != size;
}
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();
}
3、迭代器删除
迭代器删除用ArrayList$Itr.remove()规避了2的问题,从而正确遍历删除元素
List<String> list=new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
System.out.println("删除前的list:"+list.toString());
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
if("c".equals(next)){
iterator.remove();
}
}
System.out.println("删除后的list"+list.toString());