fail-fast 是一种通用的系统设计思想,一旦检测到可能会发生错误,就立马抛出异常
throw new ConcurrentModificationException();
抛出 ConcurrentModificationException
异常之后不需要捕获,直接中止程序
示例
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (Integer i : integers) {
if (i == 2) {
integers.remove(i);
}
}
}
}
/*
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.TEST.main(TEST.java:143)
*/
ArrayList 中抛出异常的位置:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
迭代器 Iterator
for-each 本质上是个语法糖,底层是通过迭代器 Iterator 配合 while 循环实现的 反编译后字节码:
ArrayList<Integer> integers = new ArrayList(Arrays.asList(1, 2, 3, 4));
Iterator var2 = integers.iterator();
while(var2.hasNext()) {
Integer i = (Integer)var2.next();
if (i == 2) {
integers.remove(i);
}
}
ArrayList 中的 Iterator 代码:
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
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];
}
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();
}
}
....
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
modCount 用来记录数组被修改次数,每次对数组进行修改的时候都会自增 1
创建 Iterator 的时候 expectedModCount = modCount ,如果在遍历的过程中增加或删除了其中一个元素,那这两个字段就不相等了,在迭代器执行 next 方法之后执行 checkForComodification 检查,抛出 ConcurrentModificationException 异常
ArrayList 中的 add 和 remove 方法中有 modcount++ 的代码
这种机制可以保证迭代器在遍历 ArrayList 时,不会遗漏或重复元素,同时也可以在多线程环境下检测到并发修改问题。
如何正确删除
Remove 后 break
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (Integer i : integers) {
if (i == 2) {
integers.remove(i);
break;
}
}
System.out.println(integers);
}
}
调用 remove 之后直接 break 掉,不会调用 next 函数,也就不会调用检查
无法删除多个重复元素了
for 循环
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (int i = 0; i < integers.size(); i++) {
if (integers.get(i)== 2){
integers.remove(i);
}
}
System.out.println(integers);
}
}
for 循环虽然可以避开 fail-fast 保护机制,但有问题
remove 函数会将被删除元素的后面的所有元素前移一位,这样就下标就跳过了一位没有检查
iterator 方式(推荐)
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
Iterator<Integer> iterator = integers.iterator();
while(iterator.hasNext()){
if (iterator.next()==2){
iterator.remove();
}
}
}
}
- 使用 iterator 可以 remove 的原因是 ArrayList 的迭代器的 remove 方法中对 expectedModCount = modCount 进行过了修改
Stream 方式(推荐)
Java 8 后可使用
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
integers = integers.stream()
.filter(i -> i != 2)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(integers);
}
}
removeIf 函数 (推荐)
Java 8 后可使用
class TEST {
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
integers.removeIf(i->i==2);
System.out.println(integers);
}
}
removeIf
函数是 Java8 后在 Collection 接口中添加的 default 方法,也是用迭代器 Iterator 实现的removeIf
接口中传入的是一个函数式接口,可以用 lamba 表达式和方法引用ArraysList 中的 removeIf 函数重写了 Collection 接口中的默认方法