[Java拾遗]迭代list过程中删除元素

本文探讨了Java List在迭代过程中删除元素时可能引发的并发异常,并通过实例代码解析了其背后的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[size=medium]今天在翻看HDFS中FSImage初始化部分时,其中有段代码是这样的:[/size]

for (URI dirName : fsNameDirs) {
boolean isAlsoEdits = false;
for (URI editsDirName : fsEditsDirs) {
if (editsDirName.compareTo(dirName) == 0) {
isAlsoEdits = true;
fsEditsDirs.remove(editsDirName);
break;
}
}
}

[size=medium]根据以往经验,这段代码可能会有ConcurrentModificationException发生。在向他们指出这个问题前,我写了简单几句测试代码来提前验证下。

代码一:[/size]

List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");

for (String str : list) {
if (str.equals("a")) {
list.remove(str);
}
}

System.out.println(list);

[size=medium]代码一的结果如预期抛出异常。因为在迭代过程中再去删除元素,会造成迭代索引有问题。于是我又随手修改了下判断条件,删除list不同位置的元素。大家看下面这个例子,它的结果应该是什么?


代码二:[/size]

List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");

for (String str : list) {
if (str.equals("b")) {
list.remove(str);
}
}

System.out.println(list);

[size=medium]代码二的运行结果是正常的:[a, c]。

这让我比较迷惑了。接着试了几次后发现,在一个list中,只有删除倒数第二个元素时是正常的,删除其它位置都会有异常抛出。于是我翻看了AbstractList中的迭代实现,主体是下面这三段代码[/size]


public boolean hasNext() {
return cursor != size();
}

public E next() {
checkForComodification();
try {
E next = get(cursor++);
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

[size=medium] cursor标示着当前list的索引,不断地与list size比对。如果hasNext返回true,会紧接着执行next方法,在next方法中检查当前list有没有被修改过。

在list迭代过程中,如果删除一个元素,那么size就减一,hasNext提前结束,迭代不会到达list的最后一个元素。也就是说,如果在迭代到list倒数第二个元素时删除此元素,接下来的hasNext会返回false,迭代结束,不会进入next方法中做检查,也就不会出什么问题。而除此之外的其它情况下,hasNext都是true,接下来的next方法检查时会产生异常。

问题让我很疑惑,但分析后的原理很简单。惟一感觉与平时想法不一样的地方是它对hasNext的判断条件有些奇怪。它没有以cursor小于list size来判断,而是取它俩是否相等,在cursor超过size时,又在从list中get元素时施以IndexOutOfBound的弥补。[/size]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值