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

Java中遍历ArrayList的正确方式与注意事项
文章详细介绍了在Java中遍历ArrayList的三种常见方式:for循环、Iterator迭代器和foreach,并分析了每种方式在边遍历边修改列表时的潜在问题。for循环可能引发漏遍历或重复遍历,Iterator允许安全删除但不允许添加,而foreach实际上是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底层_肥肥技术宅的博客-优快云博客)

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

数组类型列表遍历会执行

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

集合类型列表遍历会执行

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

}

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

迭代遍历是一种重要的编程概念,在不同场景下有着广泛的应用。 ### 迭代遍历的概念 迭代是Collection集合元素的通用获取方式,在取元素之前要先判断集合中有没有元素,如果有,就把这个元素取出来,继续进行判断,如果还有就再取出来,一直把集合中的所有元素取出,这种取出方式就叫做迭代 [^2]。在Java集合框架中,迭代器遍历集合元素的标准方式,也是设计模式中迭代器模式的经典实现 [^1]。 ### 迭代遍历的方法 - **Java集合迭代**:在Java里,通过迭代器Iterator)来实现集合的迭代遍历。可以使用`hasNext()`方法判断是否还有元素,`next()`方法获取下一个元素。示例代码如下: ```java import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); } } } ``` - **二叉树迭代遍历**:对于二叉树的迭代遍历,有前序、中序后序三种常见方式。以Python实现二叉树的前序迭代遍历为例: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def preorderTraversal(root): if not root: return [] stack, result = [root], [] while stack: node = stack.pop() result.append(node.val) if node.right: stack.append(node.right) if node.left: stack.append(node.left) return result ``` ### 迭代遍历的应用 - **集合元素处理**:在Java等编程语言中,迭代器可用于遍历集合(如List、Set等),对集合中的每个元素进行特定操作,如查找、替换、删除等 [^1]。 - **二叉树操作**:二叉树的迭代遍历在不同场景有不同应用。前序遍历可用于复制二叉树、前缀表达式求值;中序遍历适用于二叉搜索树排序;后序遍历可用于计算树深度、释放节点内存 [^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

昆仑双开分身

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

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

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

打赏作者

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

抵扣说明:

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

余额充值