刚才看到一个问题,关于List和Set集合中iterator的fail-fast特性
先上代码:
List集合:
public class Test {
public static void main(String[] args){
List<String> L = new LinkedList();
L.add("aaa");
L.add("bbb");
L.add("ccc");
System.out.println(L);
String delete = "bbb";
for(Iterator<String> iter=L.iterator();iter.hasNext();){
String now = iter.next();
System.out.println(now);
if(now==delete)
L.remove(delete);
}
System.out.println(L);
}
}
运行无异常,List在用iterator遍历过程中,可以用List的remove方法删除其最后两个元素任意一个(不能同时删除
![吐舌头](http://static.blog.csdn.net/xheditor/xheditor_emot/default/tongue.gif)
Set集合:
public class Test {
public static void main(String[] args){
Set<String> S = new LinkedHashSet();
S.add("aaa");
S.add("bbb");
S.add("ccc");
System.out.println(S);
String delete = "bbb";
for(Iterator<String> iter=S.iterator();iter.hasNext();){
String now = iter.next();
System.out.println(now);
if(now==delete)
S.remove(delete);
}
System.out.println(S);
}
}
当在用iterator遍历过程中,用Set的remove方法删除倒数第二个元素时运行出现异常:
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(Unknown Source)
at java.util.LinkedHashMap$LinkedKeyIterator.next(Unknown Source)
at nowcoder.Test.main(Test.java:18)
用Set的remove方法删除倒数第1个元素
public class Test {
public static void main(String[] args){
Set<String> S = new LinkedHashSet();
S.add("aaa");
S.add("bbb");
S.add("ccc");
System.out.println(S);
String delete = "ccc";
for(Iterator<String> iter=S.iterator();iter.hasNext();){
String now = iter.next();
System.out.println(now);
if(now==delete)
S.remove(delete);
}
System.out.println(S);
}
}
运行正常:
[aaa, bbb, ccc]
aaa
bbb
ccc
[aaa, bbb]
终于到了解决问题的时候了。。。
1、首先找到List和Set各自的iterator实现源代码
List:LinkedList->AbstractSequentialList找到
/**
* Returns an iterator over the elements in this list (in proper
* sequence).<p>
*
* This implementation merely returns a list iterator over the list.
*
* @return an iterator over the elements in this list (in proper sequence)
*/
public Iterator<E> iterator() {
return listIterator();
}</span>
接着找。。。到
AbstractList找到(由于太长,已删除无关代码)
/**
* {@inheritDoc}
*
* <p>This implementation returns {@code listIterator(0)}.
*
* @see #listIterator(int)
*/
public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {//
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
}
由上面代码可以看出当List用iterator遍历到倒数第二个元素时(已调用next方法),cursor=2,用remove方法删除会使得size()减1变为2,于是当iter调用hasNext方法时,cursor==size(),返回false,结束遍历,无异常。
当List用iterator遍历到倒数第一个元素时(已调用next方法),cursor=3,用remove方法删除会使得size()减1变为2,于是当iter调用hasNext方法时,cursor!=size(),本应返回true,返回的却是false???结束遍历,无异常。我也不知为什么。。。求解
Set:寻找路径:LinkHashSet->HashSet
public Iterator<E> iterator() {
return map.keySet().iterator();
}
->HashMap$KeySet->HasMap$KeyIterator
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
->HashMap$HashIterator
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);//指向第一个不为空的元素
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);//<span style="font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;">指向下一个不为空的元素</span>
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
从上面代码中可以看出,当Set用iterator遍历到倒数第二个元素时(已调用next方法),next(Node类型)指向倒数一个元素(节点),不为null,用remove方法删除会使得modCount加一而expectedModCount不变,于是
modCount = expectedModCount+1 =》 modCount != expectedModCount
当iter调用hasNext方法时,next!=null ,返回true,调用next方法,即调用nextNode方法,抛出异常。
如果是在遍历到倒数第一个元素时,next=null,所以无论你干什么都可以,反正下一次hasNext会返回false。
这样看来,只要不是在遍历倒数第一个元素时,修改Set,都会使得modCount != expectedModCount,从而抛出异常。