什么是fail-fast
百度百科 上是这么写的: fail-fast
是Java集合(Collection)中的一种错误机制. 我觉得不太全面.
下面来看一下维基百科 上这怎么写的:
In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process. Such designs often check the system’s state at several points in an operation, so any failures can be detected early. The responsibility of a fail-fast module is detecting errors, then letting the next-highest level of the system handle them.
从上面这段话就能看出来, fail-fast
是在系统设计当中的一种错误检测机制 , 一旦检测到可能 发生错误, 就立即抛出异常, 程序不再继续运行.
集合中的fail-fast
首先, 来复现这个错误:
1
2
3
4
5
6
7
8
9
10
11
12
List < String > nameList = new ArrayList < String > () {{
add ( "张三" );
add ( "李四" );
add ( "小王" );
add ( "丑八怪" );
}};
for ( String name : nameList ) {
if ( name . equals ( "丑八怪" )) {
nameList . remove ( name );
}
}
运行上面的代码就会抛出这样的异常:
1
2
3
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)
上面的代码是在for-each
中要删除集合中的元素而抛出的异常. 同样的, 增加(add()
)元素也会抛出这个异常.
异常产生的原因
来看一下源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public boolean add ( E e ) {
ensureCapacityInternal ( size + 1 ); // Increments modCount!!
elementData [ size ++] = e ;
return true ;
}
private void ensureCapacityInternal ( int minCapacity ) {
ensureExplicitCapacity ( calculateCapacity ( elementData , minCapacity ));
}
private void ensureExplicitCapacity ( int minCapacity ) {
modCount ++ ;
// ...
}
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 ++ ;
// ...
}
从这段代码可以发现, 对ArrayList
的add()
, remove()
, clear()
方法, 只要涉及到改变集合中的元素的个数的方法都会导致modCount
的改变, 但是没有对expectedModCount
进行改变.
1
2
3
4
final void checkForComodification () {
if ( expectedModCount != modCount )
throw new ConcurrentModificationException ();
}
当expectModCount
和modCount
不同的时候, 就会抛出一开始出现的异常.
避免产生异常
1. 使用普通for
循环进行操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List < String > nameList = new ArrayList < String > () {{
add ( "张三" );
add ( "李四" );
add ( "小王" );
add ( "丑八怪" );
}};
for ( int i = 0 ; i < nameList . size (); i ++ ) {
String name = nameList . get ( i );
if ( name . equals ( "丑八怪" )) {
nameList . remove ( name );
}
}
这样虽然不会报错, 但是可能会产生漏删的情况出现.
2. 使用Iterator
进行操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List < String > nameList = new ArrayList < String > () {{
add ( "张三" );
add ( "李四" );
add ( "小王" );
add ( "丑八怪" );
}};
Iterator < String > iterator = nameList . iterator ();
while ( iterator . hasNext ()) {
if ( iterator . next (). equals ( "丑八怪" )) {
iterator . remove ();
}
}
3. 其实使用for-each
也可以
1
2
3
4
5
6
7
8
9
10
11
12
13
List < String > nameList = new ArrayList < String > () {{
add ( "张三" );
add ( "李四" );
add ( "小王" );
add ( "丑八怪" );
}};
for ( String name : nameList ) {
if ( name . equals ( "丑八怪" )) {
nameList . remove ( name );
break ;
}
}
当集合中只删除一个元素的时候, 在删除操作之后立即break
, 使循环不进入下一次遍历就可以了.
4. 使用fail-safe
的集合类
5. 使用stream中的filter