这是对我上一个问题的跟进:
Collection - Iterator.remove() vs Collection.remove()
以下两段代码,显然只有一行不同,但有一行抛出异常而其他则没有。你能解释一下这个区别吗?
List<String> list = new ArrayList<String>
(Arrays.asList("noob1","noob2","noob3"));
System.out.println(list);
for (String str : list) {
if (str.equals("noob2")) {
list.remove(str);
}
}
运行正常,但如果我将条件更改为
if (!str.equals("noob2"))
代码抛出异常!
答案 0 :(得分:4)
在这种情况下,您要移除第二个列表元素。
List<String> list = new ArrayList<String>
(Arrays.asList("noob1", "noob2", "noob3", "noob4"));
System.out.println(list);
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String str = iterator.next();
if (str.equals("noob3")) {
System.out.println("Checking "+str);
list.remove(str);
}
}
System.out.println(list);
打印
[noob1, noob2, noob3, noob4]
Checking noob1
Checking noob2
Checking noob3
[noob1, noob2, noob4]
通过删除第二个最后一个元素,您已将大小缩小为迭代过的元素数。
// from ArrayList.Itr
public boolean hasNext() {
return cursor != size;
}
这导致循环在next()
中执行并发修改检查之前提前退出。如果您删除任何其他元素next()
,则会获得CME。
BTW还绕过支票的东西是
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String str = iterator.next();
System.out.println("Checking "+str);
if (str.equals("noob2")) {
list.remove("noob1");
list.remove("noob3");
}
}
只要集合的大小与其所在的索引相同,就不会执行检查。
答案 1 :(得分:1)
for循环只是列表迭代器扫描的简化语法。如果在其下修改了列表,迭代器可能会抛出异常,但不能保证。由于hasNext,迭代器通常会提前一个元素,这使得第一个案例不太可能受到列表修改的影响。当“noob2”被删除时,迭代器已经知道“noob3”。
答案 2 :(得分:1)
我认为异常是因为您正在尝试更改正在循环的集合...而不是因为if
条件。
我建议您创建一个仅包含验证条件的项目的新列表。将它们添加到新列表中,避免更改原始集合。
答案 3 :(得分:1)
实际上,你不应该在“随意”迭代过程中删除集合的元素。当您必须在某个循环中修改集合时,必须使用iterator
来执行这些操作。
public class Test {
public static void main(String... args) {
List<String> list = new ArrayList<String>(Arrays.asList("noob1", "noob2", "noob3"));
System.out.println(list);
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String str = iterator.next();
if (!str.equals("noob2")) {
iterator.remove();
}
}
System.out.println(list);
}
}
答案 4 :(得分:1)
这是因为您正在尝试从当前正在迭代的Collection
中删除。做一个小改动你可以做你想做的事:
String[] strValues = {"noob1","noob2","noob3"}; // <<< Array
List<String> list = new ArrayList<String>(Arrays.asList(strValues));
System.out.println(list);
for (String str : strValues) { // << List is duplicate of array so can iterate through array
if (!str.equals("noob2")) {
list.remove(str);
}
}
那应该有用。希望
答案 5 :(得分:0)
好吧,你的第一个案例不会抛出异常,因为当你删除索引1处的元素时,迭代器会在索引2处为Iterator.hasNext()返回false。
Iterator<String> itr = list.iterator();
while(itr.hasNext()){
String s= itr.next();
if(s.equals("noob2")){
list.remove(s); // size of the list is 2 here
System.out.println(itr.hasNext());// this returns false as it doesn't have anything at index 2 now.(on 2nd iteration )
}
}
您可以使用简单的for循环清楚地测试它:
for (int i=0; i<list.size(); i++) {
if (list.get(i).equals("noob2")) {
System.out.println(list.get(i));
System.out.println(list.size());
list.remove(list.get(i));
System.out.println(list.size());
}
}
输出:
[noob1, noob2, noob3]
noob2
3
2
请注意删除元素后列表的大小,该元素在递增后失败。 2&lt; 2 这是假的