java foreach iterator remove,for、foreach、iterator遍历时修改、删除、增加的问题

Java中,一般存在三种遍历方式:

for

foreach

iterator

for循环

修改:遍历中修改,不会存在任何问题

删除:遍历中删除某个元素,集合/数组的长度就会变短,这样在遍历时就有可能造成数组下标越界异常(注意,for循环的list.size每次遍历都会获取,所以如果删除一个中间元素,遍历并不会出现下标越界;下表越界只会发生在同一循环内,比如已经删除了最后一个元素,却又获取最后一个元素的情况)

// 代码示例如下

// 不会抛出异常

for (int i = 0; i < list.size(); i++) {

if (i == 0) {

list.remove(0);

}

System.out.println(list.get(i));

}

// 会抛出数组下标越界

for (int i = 0; i < list.size(); i++) {

if (i == 2) {

list.remove(0);

}

System.out.println(list.get(i));

}

添加:遍历中一直添加元素,有可能造成无穷循环的情况

// 代码示例如下

for (int i = 0; i < list.size(); i++) {

if (i == list.size() - 1) {

list.add("a");

}

}

foreach 与 iterator

foreach 实际上只是 iterator的一个语法糖,如果由如下foreach代码:

for (String item : items) {

System.out.print(item);

}

实际上编译后的代码为:

Iterator iterator = items.iterator();

while (iterator.hasNext()) {

System.out.print(iterator.next());

}

修改:使用iter在遍历时修改,与for循环一样,不会发生任何问题

删除:使用iter在遍历时删除元素,如果使用的是iterator.remove()方法是可以正常删除的:

// 情况一: 使用集合提供的remove方法:

for (String s : list) { // 抛出ConcurrentModificationException 抛出并发修改异常,无法删除

if (s.equals("0")) {

list.remove(0);

}

System.out.println(s);

}

// 情况二:删除倒数第二个元素,不会报错,因为下次hasNext返回false直接退出循环

Iteratoriterator = list.iterator();

while (iterator.hasNext()) {

String value = iterator.next();

if ("1".equals(value)) {

list.remove(list.size() - 2); // 删除元素1

}

System.out.println(value); // 0, 1

}

System.out.println(list); // [0, 2]

// 情况三:使用iterator提供的remove方法:

Iteratoriterator = list.iterator();

while (iterator.hasNext()) {

String value = iterator.next();

if ("0".equals(value)) {

iterator.remove(); // 不会抛出异常,会正常删除

}

System.out.println(value); // 还是会打印出被删除的元素 0,1,2

}

System.out.println(list); // 输出的元素确实是删除过后的 [1,2]

增加:增加无论任何情况下,都会抛出ConcurrentModificationException异常

Iterator的源码

参考类: ArrayList$Itr

// 源码如下:

private class Itr implements Iterator {

int cursor; // 下次调用next()获取的元素的指针

int lastRet = -1; // 最后一次操作的索引,如果上次操作为remove,则会置为-1 是一个记录变量

int expectedModCount = modCount; // modCount代表当前列表被修改的次数,使用expectedModCount记录当前状态的修改次数

public boolean hasNext() { // 如果当前指针大小小于list的size大小,说明还有元素没有遍历完

return cursor != size;

}

// 获取下一个元素

@SuppressWarnings("unchecked")

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; // 获取cursor位置元素成功,将指针位置后移一位(cursor++)

return (E) elementData[lastRet = i]; // 使用lastRet记录上一次操作的位置

}

// 移除当前元素

public void remove() {

// 如果上次操作为remove,则抛出非法参数异常,所以循环直接使用iterator.remove() 就会抛出 IllegalStateException

if (lastRet < 0)

throw new IllegalStateException();

checkForComodification();

try {

// 调用当前集合的remove()方法,移除当前的元素

ArrayList.this.remove(lastRet);

cursor = lastRet; // 指针变为上次操作的索引(删除,数组后面的元素会向前移动,下次遍历的元素位置是coursor+1)

lastRet = -1; // 标记上次操作为remove

expectedModCount = modCount; // 更新当前修改次数

} catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

@Override

@SuppressWarnings("unchecked")

public void forEachRemaining(Consumer super E> consumer) {

Objects.requireNonNull(consumer);

final int size = ArrayList.this.size;

int i = cursor;

if (i >= size) {

return;

}

final Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length) {

throw new ConcurrentModificationException();

}

while (i != size && modCount == expectedModCount) {

consumer.accept((E) elementData[i++]);

}

// update once at end of iteration to reduce heap write traffic

cursor = i;

lastRet = i - 1;

checkForComodification();

}

// 检查修改次数是否发生变化: 即检测list是否被修改

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值