Java中 普通for循环, 增强for循环( foreach) List中增删改查的注意事项

文章目录

俩种循环

Java中 普通for循环, 增强for循环( foreach) 俩种List的遍历方式有何异同,性能差异?

普通for循环(使用索引遍历):

java 复制代码
for (int i = 0; i < list.size(); i++) {  
    Object item = list.get(i);  
    // 处理item  
}

这是最基本的遍历方式,它使用索引来访问列表中的每一个元素。

增强for循环(也称为"foreach"循环):

java 复制代码
for (Object item : list) {  
    // 处理item  
}

这种循环在Java 5中被引入,作为对集合遍历的语法糖。在内部,它仍然使用Iterator,但语法更为简洁。很多开发者也称之为"foreach"循环,但实际上在Java中并没有名为"foreach"的关键字;这是C#中的一个关键字。在Java中,这只是增强for循环的一种常见称呼。

异同点:

普通for循环:

需要显式地通过索引来访问元素。

可以方便地访问和修改当前索引位置的元素。

对于List的随机访问操作,性能是高效的,因为ArrayList等基于数组的列表支持快速的随机访问。

增强for循环:

语法简洁,不需要关心索引。

只能访问元素,不能方便地修改元素 (除非元素是可变的对象,并且你修改了对象的内部状态)。
在内部,它使用Iterator ,所以对于不支持快速随机访问的数据结构(如LinkedList),它的性能可能更优。

性能差异:

对于ArrayList等基于数组的列表:普通for循环通常会比增强for循环稍微快一点,因为它直接通过索引访问元素,避免了Iterator的开销。但这种差异在大多数情况下是微不足道的,除非列表非常大或者这段代码是性能瓶颈。

对于LinkedList等不支持快速随机访问的列表:增强for循环可能会更有优势,因为它内部使用Iterator,这与LinkedList的迭代访问方式相匹配。

遍历

ArrayList 情况下,普通for循环遍历就是最基础的for循环,而foreach底层是使用迭代器。

增加

删除

1 根据index删除

java 复制代码
 List<String> list = new ArrayList<>(4);
        list.add("a");
        list.add("ab");
        list.add("abc");
        list.add("abcd");
java 复制代码
 //错误方式 根据下标remove  数组形式 普通for循环
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).contains("a")) {
                list.remove(i);
            }
        }

ab, abcd

读者可能回想,怎么不是空的list呢?不妨让我们看下remove这个方法。(这个是ArrayList 里面的实现)

java 复制代码
  public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

其中

java 复制代码
 System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);

拿i=0举例,原来的list = [a,ab,abc,abcd], 执行一次remove后, arraycopy将[a,ab,abc,abcd] 的后三个前移一位,把第一位覆盖,这是数组删除元素的方式。这样一来,原list就变成[ab,abc,abcd],第二次循环的时候i=1,此时list.get(1) = abc,直接跳过了ab,所以最后没有达到我们预期的空[].

正确的做法是使用迭代器

java 复制代码
//正确方式 迭代器
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().contains("a")) {
                // 删除元素
                iterator.remove();
            }
        }

2 根据对象删除

java 复制代码
//根据对象删除
        List<String> list2 = new ArrayList<>();
        list2.add("111");
        list2.add("222");
        list2.add("222");
        list2.add("333");

正确 普通for循环

java 复制代码
//
        for (int i = 0; i <list2.size(); i++) {
            list2.remove("222");
        }

错误,增强for循环 抛异常

java 复制代码
  //
        for (String s : list2) {
            list2.remove("222");
        }

原因:

迭代器内部的每次遍历都会记录List内部的modcount当做预期值,然后在每次循环中用预期值与List的成员变量modCount作比较,但是普通list.remove调用的是List的remove,这时modcount++,但是iterator内记录的预期值并没有变化,所以会报错。

如果想要删除元素的话需要使用迭代器内部的remove方法。

修改

foreach不可以删除/修改集合元素,而for可以

foreach和for都可以修改元素(对象)里面的属性

相关推荐
浮游本尊1 小时前
Java学习第22天 - 云原生与容器化
java
渣哥3 小时前
原来 Java 里线程安全集合有这么多种
java
间彧3 小时前
Spring Boot集成Spring Security完整指南
java
间彧3 小时前
Spring Secutiy基本原理及工作流程
java
Java水解4 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆6 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学7 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole7 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端
华仔啊7 小时前
基于 RuoYi-Vue 轻松实现单用户登录功能,亲测有效
java·vue.js·后端