今天组长提了一个问题注意点,说删除Set元素的时候,如果按照一下代码则会抛出异常:
Set
<
Object
>
bb
=
new
HashSet
<
Object
>
();
bb.add( " 23242 " );
bb.add(Integer.valueOf( 45 ));
Iterator it = bb.iterator();
while (it.hasNext()) {
Object ele = it.next();
bb.remove(ele); // wrong
}
bb.add( " 23242 " );
bb.add(Integer.valueOf( 45 ));
Iterator it = bb.iterator();
while (it.hasNext()) {
Object ele = it.next();
bb.remove(ele); // wrong
}
异常如下:
Exception in thread
"
main
"
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java: 793 )
at java.util.HashMap$KeyIterator.next(HashMap.java: 827 )
at com.learn.test.Test.testIteratorRemove(Test.java: 118 )
at com.learn.test.Test.main(Test.java: 102 )
at java.util.HashMap$HashIterator.nextEntry(HashMap.java: 793 )
at java.util.HashMap$KeyIterator.next(HashMap.java: 827 )
at com.learn.test.Test.testIteratorRemove(Test.java: 118 )
at com.learn.test.Test.main(Test.java: 102 )
从上可以看出异常是从HashMap的内部类抛出的。
private
abstract
class
HashIterator
<
E
>
implements
Iterator
<
E
>
{
Entry < K,V > next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry < K,V > current; // current entry
.....
final Entry < K,V > nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); // 抛出异常
Entry < K,V > e = current = next;
if (e == null )
throw new NoSuchElementException();
if ((next = e.next) == null ) {
Entry[] t = table;
while (index < t.length && (next = t[index ++ ]) == null )
;
}
return e;
}
..........
}
Entry < K,V > next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry < K,V > current; // current entry
.....
final Entry < K,V > nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException(); // 抛出异常
Entry < K,V > e = current = next;
if (e == null )
throw new NoSuchElementException();
if ((next = e.next) == null ) {
Entry[] t = table;
while (index < t.length && (next = t[index ++ ]) == null )
;
}
return e;
}
..........
}
我们都知道HashSet其实只基于HashMap来实现的。看HashSet的remove实现方法:
public
boolean
remove(Object o) {
return map.remove(o) == PRESENT;
}
return map.remove(o) == PRESENT;
}
再来看HashMap的remove方法:
public
V remove(Object key) {
Entry < K,V > e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
// 先取得key的hashcode,根据hashcode在哈希表中找到对应的index,然后在对应的index再遍历table[i]指向的链表,这个链表一般只有一个元素
final Entry < K,V > removeEntryForKey(Object key) {
int hash = (key == null ) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry < K,V > prev = table[i];
Entry < K,V > e = prev;
while (e != null ) {
Entry < K,V > next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount ++ ;
size -- ;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval( this );
return e;
}
prev = e;
e = next;
}
return e;
}
Entry < K,V > e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
// 先取得key的hashcode,根据hashcode在哈希表中找到对应的index,然后在对应的index再遍历table[i]指向的链表,这个链表一般只有一个元素
final Entry < K,V > removeEntryForKey(Object key) {
int hash = (key == null ) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry < K,V > prev = table[i];
Entry < K,V > e = prev;
while (e != null ) {
Entry < K,V > next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount ++ ;
size -- ;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval( this );
return e;
}
prev = e;
e = next;
}
return e;
}
其实注意到其中间有这么一段代码:modCount++; modCount是这个HashMap结构修改的次数,比如增加,删除等等,再来看上面HashIterator中的expectedModCount,这个是Iterator中期望HashMap元素修改次数,初始值即modCount,HashMap的remove方法执行时,并没有同步当然也无法同步这两个变量值一致,才导致了上面的异常。找到了抛出异常的原因,很明显不能通过这个方法来删除Set中的元素,我们注意到HashIterator也有remove方法,代码如下:
public
void
remove() {
if (current == null )
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null ;
HashMap. this .removeEntryForKey(k);
expectedModCount = modCount; //删除完同步两个变量的值
}
if (current == null )
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null ;
HashMap. this .removeEntryForKey(k);
expectedModCount = modCount; //删除完同步两个变量的值
}
所以尝试一下代码:
Set
<
Object
>
bb
=
new
HashSet
<
Object
>
();
bb.add( " 23242 " );
bb.add(Integer.valueOf( 45 ));
Iterator it = bb.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
bb.add( " 23242 " );
bb.add(Integer.valueOf( 45 ));
Iterator it = bb.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
执行成功。所以,执行HashSet的remove方法时,是不可以用Iterator方法遍历一个一个删除的。但是HashSet的remove方法也可以执行成功,就是Set中只有一个元素的时候,因为初始的时候expectedModCount和modCount是相等的。