我有两个java类
import java.util.*;
public class ArrayListTest032 {
public static void main(String[] ar) {
List<String> list = new ArrayList<String>();
list.add("core java");
list.add("php");
list.add("j2ee");
list.add("struts");
list.add("hibernate");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
list.remove("php");
while (itr.hasNext()) {
System.out.println(itr.next());
}
}
}
当我运行上面的代码时,我得到低于输出。
core java
php
j2ee
struts
hibernate
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at ArrayListTest032.main(ArrayListTest032.java:20)
这是预期的,因为我在迭代时修改列表。但是在下面的java类中,set family执行相同的逻辑。
import java.util.*;
public class HashSetTest021 {
public static void main(String[] ar) {
Set<String> set = new HashSet<String>();
set.add("core java");
set.add("php");
set.add("j2ee");
set.add("struts");
set.add("hibernate");
Iterator<String> itr = set.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
set.remove("php");
while (itr.hasNext()) {
System.out.println(itr.next());
}
}
}
出来就是。
hibernate
core java
j2ee
php
struts
没有任何 ConcurrentModificationException的。
我只是想知道为什么同一段代码抛出 ConcurrentModificationException的 的情况下 list
家庭,但没有任何 ConcurrentModificationException的 的情况下 set
家庭
这是实现上的差异:数组列表返回的迭代器检测并发修改,即使它位于最后,因为它检查长度;的迭代器 HashSet
, TreeSet
和 LinkedList
另一方面,不检测这种情况,因为它们在检查并发修改之前检查是否位于最后。该文档允许迭代器不要抛出并发修改,因此两种方法都是有效的。
这是实现上的差异:数组列表返回的迭代器检测并发修改,即使它位于最后,因为它检查长度;的迭代器 HashSet
, TreeSet
和 LinkedList
另一方面,不检测这种情况,因为它们在检查并发修改之前检查是否位于最后。该文档允许迭代器不要抛出并发修改,因此两种方法都是有效的。
这是一种“逆行”行为,因为迭代器一旦完全遍历,就不能重复使用,也就是说 hasNext
到达列表末尾时,方法应返回false。
但在这种情况下,迭代器返回 ArrayList.iterator
是一个内部实现类,代码为 hasNext
如下:
public boolean hasNext() {
return cursor != size;
}
所以当你打电话 hasNext
在第二个循环中,它表示(错误地)有更多项要迭代,因为您在第一次迭代后执行了更改列表大小的操作。在语义上,您应该无法在到达结尾后继续迭代列表中的项目,但由于此实现细节,它允许您继续第二个while循环。当然,此时,由于您在后备列表中所做的更改,您将获得并发修改异常。
另一方面,哈希集使用的迭代器有它的 hasNext
实施如下:
public final boolean hasNext() {
return next != null;
}
这种实现不会像在迭代完成后对哈希集所做的修改那样“易受攻击”,因此也是如此 hasNext
方法表现得更好。
首先阅读 的JavaDoc 对于迭代器。它提到了吗? ConcurrentModificationException
地方?
现在,阅读JavaDoc for ConcurrentModificationException的,并注意以下内容(重点补充):
这个例外 可能 被检测到并发修改对象的方法抛出 何时不允许进行此类修改。
现在仔细看看你的代码。你的 while
循环遍历集合的所有元素(即使第一个示例的输出未指示此情况,这告诉我您已编辑输出或这不是您的实际代码)。在删除元素时,没有其他项可以迭代,因此第二个循环应该总是立即退出。
因此,结论是列表迭代器的实现者具有 选择 即使在没有更多元素要迭代的情况下抛出该异常,而set迭代器的实现者也是如此 选择 不要。根据规格,这两种情况都完全可以接受。
public static void main(String[] ar) {
List<String> list = new ArrayList<String>();
list.add("core java");
list.add("php");
list.add("j2ee");
list.add("struts");
list.add("hibernate");
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
list.remove("php");
/* while (itr.hasNext()) {
System.out.println(itr.next());
}*/
}
problem in itr object.it holds the list object reference
如果你通过迭代器对集合做任何事情,Hashset可以抛出ConcurrentModificationException。然而,围绕迭代器的快速失败行为有很多启发式方法,目标是尽可能完成迭代。 JavaDocs似乎很清楚它的行为。
如果是列表 当我们用第一个循环遍历它时
Iterator itr = set.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
游标值和大小将变为相同.Cursor包含遍历的元素的总数和内部的hashNext()方法,用于列表遍历的代码包含以下代码:
public boolean hasNext() {
return cursor != size;
}
所以在第一次while循环游标== size.But之后从列表大小中删除元素变为(originalSize-1)。对于下一个while循环它进入内部而在itr.next()方法内部它检查modcount修改并抛出ConcurrentModificationException。
在Set的情况下,它为每个itr.hasnext()调用检查下一个!= null。在遍历第一个while循环后,next变为null。从set中移除元素不会影响下一个值为null并且itr.hasNext将返回next == null为true,因此它不会进入while循环以检查modcount修改。因此它不会抛出ConcurrentModification异常。