fail-fast机制
fail-fast(快速失败)机制在Java集合中都存在,本文就ArrayList
分析。
fail-fast是什么?
先从ArrayList的官方解释看看:
/**
* 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.
*
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
*
*/
翻译:
在迭代器创建之后,集合有机构性修改,除非使用迭代器自身的add
,remove
动作,使用ArrayList上的方法对集合中数据进行任何操作
(包括查询和修改),
都会触发快速失败
机制,表现为抛出ConcurrentModificationExeception
异常…
迭代器的快速失败并不能保证,即存在有错误发生也没触发的情况,所以,不该依赖这个依赖异常来写程序,仅是用来检查程序是否有bug
通俗的个人理解:
快速失败机制,对于集合上的操作,比如查询,修改,若数据出现不确定的情况,便不再返回一个不确定的结果,而是直接抛出异常,让用户尽快
知道有错误发生了。符合程序的确定性
的原则。
例子:
遍历过程中使用ArrayList的方法删除元素,在下一次调用迭代器的next()方法时,抛出异常
//初始一个集合并设置数据
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
list.add(i+"");
}
//形式1:增强型For遍历
for ( String e :list) { //以增强For来遍历集合时,会调用集合内部的迭代器
if (e.equals("1")) { //遍历过程中使用ArrayList的方法删除元素,在下一次调用迭代器的next()方法时,抛出异常
list.remove(e);
}
System.out.println(e);
}
Java中,若是使用增强型For,遍历集合类时,本质使用其内部的迭代器来遍历
//形式2:增强型For的本质形式(去语法糖)
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String e = lit.next();
if (e.equals("1")) {
list.remove(e); //使用ArrayList的remove方法
}
System.out.println(e);
}
正确用法
//形式3,使用自身的迭代器删除,不会抛出异常
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String e = lit.next();
if (e.equals("1")) {
lit.remove(); //调用迭代器自身的remove方法,
}
System.out.println(e);
}
内部实现原理:
List类中有一个变量modCount
,记录当前集合结构性修改的次数,(比如增加元素,删除元素都会导致结构性修改),初始为0,在add(),
remove()方法中都会调用modCount++
。
迭代器Iterator中有一个变量expectedModCount
,用于跟踪List中的modCount
的值,正常情况下,这两个值是保持相等的,这样可保证
迭代器能够正确无误的遍历所有的元素。若是List自己的删除一个元素,ModCount
加1,而迭代器中的ExpectedModCount
并没有做修改,
迭代器调用next()
方法时,检查到两个值不相同,即抛出异常,表示迭代器已经无法保证正确遍历元素了。
若想要遍历中删除,就要使用该迭代器
自身的remove方法,该方法会修改expectedModCount
与modCount
相等。