读书人

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

发布时间: 2012-11-03 10:57:43 作者: rapoo

[Java拾遗]迭代list过程中删除元素
今天在翻看HDFS中FSImage初始化部分时,其中有段代码是这样的:

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

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

代码一:
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);

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


代码二:
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);

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

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

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();    }}

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的弥补。 1 楼 langyu 2011-07-25 hdfs code中有break,所以没有上文所述的情况发生,但我们这里说的重点是如何next及检验不同

读书人网 >编程

热点推荐