Java foreach遍历、for循环遍历、Iterator迭代器遍历区别和遍历中操作安全问题

数据 List dataList = new ArrayList();

1、for循环

我们一般可以见到类似如下的遍历方式

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

        dataList.get(i);

}

这样的遍历一般边遍历边往列表里面加item可以往i以后加,不能往i以前加,否则加入的item就漏遍历了,而且i位置的item会遍历两遍。

比如列表[1,2,3,4] 当i=1的时候,dataList.get(i)是2,插入5便是[1,5,2,3,4],下一次i是2,dataList.get(i)还是2。

删除操作也是能删除i以后,不能删除i及i以前的item。

比如 列表 [1,2,3,4],如果遍历到i=1的时候删除了dataList.get(i)=2,那么列表就变成 [1,3,4],长度为3,i已经等于1了,下一次i加1取列表item,i就是2,dataList.get(i)就是4了。会漏遍历item值为3的item,所以边遍历边删除会有遍历不完整的问题

所以总结为:for循环遍历,如果要边遍历边删除或者增加,那么只能把数据添加到当前还没有遍历到的位置。

2.Iterator迭代器遍历

Iterator<Integer> iterable= dataList.iterator();
while (iterable.hasNext()){
    int a = iterable.next();

}

我们来看一下源码的iterator到底是什么

ArrayList的iterator是

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    
    protected int limit = ArrayList.this.size;
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor < limit;
    }
public E next() {
    if (modCount != expectedModCount)//检查迭代器记录的变更次数是否和ArrayList记录的变更次数一样,否则抛异常。也就是说遍历的时候除了迭代器,谁也不能更改列表(调用add或者remove),否则下次调用next就抛异常
        throw new ConcurrentModificationException();
    int i = cursor;
    if (i >= limit)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];//在列表的存储数组拿数据
}

public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

    try {//迭代器自己可以调用remove,调用完会同步更改次数
        ArrayList.this.remove(lastRet);
        cursor = lastRet;//移除后接着遍历移除位置的下一个数据
        lastRet = -1;
        expectedModCount = modCount;//同步更改次数
        limit--;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

可以看出 ArrayList用Iterator遍历的话,也只能用Iterator移除才行,调用Iterator移除是安全的,不会漏遍历也不会多遍历。不能添加也不能通过ArrayList对象自己移除或者添加,否则抛出异常。

全局列表多处访问或者多线程访问的时候尤其要注意,用Iterator遍历是很危险的事情,要做好同步锁和添加移除控制。

3.foreach遍历

for (int r : dataList) {
}

foreach的底层实现可以参考  肥肥技术宅 大佬的博客(foreach 循环的底层原理及正确使用方式,一定要掌握这些!_foreach底层_肥肥技术宅的博客-CSDN博客)

大概的意思就是 foreach是个语法糖,在编译阶段就会被转译。

数组类型列表遍历会执行

for(int i=0;i <size;i++)

集合类型列表遍历会执行

Iterator<Integer> iterable= dataList.iterator();
while (iterable.hasNext()){
    int a = iterable.next();

}

转来转去其实只有两种遍历,foreach只是为了简化代码书写。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闽农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值