概述
某个和迭代器相关的问题。在最近的面试和笔试中遇到多次。
ArrayList<String> integers = new ArrayList<>();
integers.add("1");
integers.add("2");
for(String s:integers){
if(s.equals("1")){
integers.remove("1");
}
if(s.equals("2")){
integers.remove("2");
}
}
请问这个程序的结果是什么。我的第一反应,迭代的过程中,直接用集合的remove修改结构,这不是会因为midCount和迭代器的expectedMidCount不一致而导致抛出并发修改异常吗?然而我还是太单纯了,直到并发修改异常顶多说明你关注过源码,但是只有分析过迭代器源码的时候,才能够回到出以上的问题——没有任何输出(无异常)
foreach循环本质上就是hashNext()+next(),而核心的点就出在hasNext的实现上了
public boolean hasNext() {
return cursor != size;
}
通过一个不等待去判断是否还具有下一个元素,其中size就是容器的大小,我们可以更改。而cursor下一个元素(严谨讲,hasNext()在next()之前调用,因此这里的cursor就是指代下一个元素的位置——类比我们去实现一个栈结构,这个就是指针指向下一个元素的位置)而当调用next()的时候,会检查两个modCount是否一致,并且将输出当前要迭代的元素arr[cursor],之后便将cursor自增。
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
现在我们有1 2 3 4 5 五个元素,假如当前调用next后,返回的是4,此时的size是5,cursor是4(元素4的索引是3,cursor自增后是4)。如果我们调用remove方法,删除某一个元素改变size。使得size == cursor 成立,那么迭代器将在倒数第二个元素就直接退出了,不会迭代最后一个元素。
不一定是倒数第二个元素,只要能在迭代到某一个位置,如果当前索引是x,那么此时的cursor是x+1.然后删除若干元素,使得size等于cursor。那么再进行下一次hasNext()的时候就会提前退出。后面的元素都不会被迭代。
再次解释最开始的代码。当倒数第二个元素被删除之后,hasNext()返回false,导致循环退出。所以只有第一个remove被执行了,而第二个remove没有被执行。
再补充一个,基本都是之前面试遇到的奇葩问题。
如果边遍历数组,边删除一个元素。(删完元素之后,遍历不能停)
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 8, 9));
for(int i=list.size()-1;i>=0;i--){
Integer cur = list.get(i);
if(cur==3)list.remove(cur);
}
System.out.println(list);
}
如果是正序删除元素,会导致后面的元素往前面补充,而导致有一个元素被跳过去,我们需要手动 index – 。或者直接使用倒序遍历的方式,当我们删除一个元素的时候,后面的内容会往前面复制,但是后面的内容我们都已经遍历过了,因此可以接着向后面进行遍历。
最后
以上就是含糊未来为你收集整理的遇到的关于Java迭代器的奇怪问题——迭代中remove不抛异常的全部内容,希望文章能够帮你解决遇到的关于Java迭代器的奇怪问题——迭代中remove不抛异常所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复