你不會得到ConcurrentModificationException
與HashSet
。這種僥倖的原因是,你碰巧在循環的迭代上添加了元素。
這遠未保證發生,因爲HashSet
沒有訂單保證。嘗試從列表中添加或刪除更多元素,並且您會發現最終能夠獲得ConcurrentModificationException
,因爲40
不會是列表中的最後一個元素。
重現這個最簡單的方法是,只是一個單一元件的作用(自那時以來,HashSet
的順序是已知的):
HashSet<Integer> hashSet = new HashSet<>();
hashSet.add(1);
for (int i : hashSet) {
hashSet.add(2);
}
這不會拋出ConcurrentModificationException
。
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
for (int i : list) {
list.add(2);
}
這將拋出ConcurrentModificationException
。
至於爲何在最後一次迭代增加了一個HashSet
,當你沒有得到異常的原因, 看看爲返回的迭代器的源代碼通過HashSet.iterator()
:
abstract class HashIterator {
// ...
public final boolean hasNext() {
return next != null;
}
// ...
}
所以,hasNext()
值是根據next
預先計算的;此設定得較低向下代碼中的一個位:
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);
//^Here
}
return e;
}
所以,當確定迭代的前一個元素被確定的hasNext
值。
由於您的代碼是:
Iterator it = marks.iterator();
while (it.hasNext()) {
int value = it.next();
if (value == 40)
marks.add(50);
}
的hasNext()
在其中添加元素的循環迭代之後被調用;但在調用it.next()
之前next
被設置爲空 - 在調用add
之前 - 因此hasNext()
將返回false,並且該循環退出而沒有ConcurrentModificationException
。
在另一方面,iterator of ArrayList
使用底層列表的大小來確定的hasNext()
返回值:
public boolean hasNext() {
return cursor != size;
//^current position of the iterator
// ^current size of the list
}
所以這個值是「活的」關於增加(或減少)底層列表的大小。因此,hasNext()
將在添加該值後返回true;所以調用next()
。但是,第一件事情是next()
確實是檢查商品化:
public E next() {
checkForComodification();
// ...
,因此是檢測到的變化,並且ConcurrentModificationException
被拋出。
請注意,您不會得到ConcurrentModificationException
與ArrayList
如果你修改了它的最後一次迭代,如果你要添加和刪除元素(或者,更確切地說,添加和刪除元素的數量相等,以便列表的大小相同)。
您發佈的代碼實際上並未進行編譯。請提供一個[mcve] ...雖然它不需要大量工作來改變它,但最好是一次完成,而不是每個讀者都必須這樣做。 –
噢,和'ArrayList'一樣''HashSet'也有同樣的錯誤... –
由於您正在使用的'HashSet'的具體實現,沒有通過'HashSet'得到它是一種僥倖。 –