java中循环的几种方式以及删除集合中的指定数据

一、for

    //for循环中删除数据 -- 方法有漏洞
    private static void deleteAtFor() {
        ArrayList<Integer> array = new ArrayList<>();
        array.add(4);
        array.add(5);
        array.add(5);
        array.add(6);
        for(int i=0;i<array.size();i++){
            if(array.get(i).equals(5)){
                array.remove(i);
            }
        }
         System.out.println(array);
    }

得到删除后的集合数据是:[4,5,6]
从结果可以看出,有一个等于5的数据没有呗被删除,
这是为什么呢?
当我们深究 for循环在集合中工作原理就可以理解,事实上,当删除一个元素时,后面元素的索引就会前进以为,如删除第一个索引为1的5的时候,此时下一个元素5的索引就变成了1,而此时for循环执行i++,对应索引的位置为2 而新的1索引上的值就不会在读取了,所以集合结果为[4,5,6],要解决这个问题很容易只需要在删除的同时把自增变量i减一,和改变后的索引对应上就好,即把 array.remove(i)改为 array.remove(i--)即可;
如下:

    //for循环中删除数据 -- 正确方法
    private static void deleteAtFor() {
        ArrayList<Integer> array = new ArrayList<>();
        array.add(4);
        array.add(5);
        array.add(5);
        array.add(6);
        for(int i=0;i<array.size();i++){
            if(array.get(i).equals(5)){
                array.remove(i--);
            }
        }
       System.out.println(array);
    }

结果为[4,6],正确!

二、forEach

    //foreach中删除数据
    private static void deleteAtForeach() {
        ArrayList<Integer> array = new ArrayList<>();
        array.add(4);
        array.add(5);
        array.add(5);
        array.add(6);
        for (Integer a:array) {
            if (4==a){
                array.remove(a);
            }
        }
    }

结果如下:

图片.png

意思是迭代器并发修改异常,出现这个原因是 当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
首先java的foreach循环其实就是根据list对象创建一个Iterator迭代对象,用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了Iterator,你如果要对list进行增删操作,都必须经过Iterator,否则Iterator遍历时会乱,所以直接对list进行删除时,Iterator会抛出ConcurrentModificationException异常
foreach迭代ArrayList时,真的不能删除元素吗,其实删除倒数第二个元素的,具体什么我们就不在这里说明了,大家可以看下这个https://blog.csdn.net/GarfieldEr007/article/details/80260822

三、迭代器

    //Iterator迭代器中删除数据
    private static void deleteAtIterator() {
        ArrayList<Integer> array = new ArrayList<>();
        array.add(4);
        array.add(5);
        array.add(5);
        array.add(6);
        Iterator<Integer>iterator = array.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals(5)){
                iterator.remove();
            }
        }
        System.out.println(array);
    }

结果是:[4, 6] ;注意点:while里只能出现一次iterator.next(),不然结果会出错。

四、用流的方式

ArrayList<Integer> array = new ArrayList<>();
        array.add(4);
        array.add(5);
        array.add(5);
        array.add(6);
        array.stream().forEach(integer -> {
            if(integer.equals(5)){
                array.remove(integer);
            }
        });
        System.out.println(array);

结果:


图片.png

stream流forEach循环也不能进行删除。具体原因不做深究,哈哈!

五、结论:

在只是遍历情况下,我们四种都可以用,在修改集合的情况下,不能用foreach和stream流forEach;

番外:

如果只是单纯的遍历的话,
从效率角度来看:For Each的效率差,stream流forEach和迭代器的效率也没有很好。for循环最优,因为ArrayList通过数组来实现,数组通过索引来定位的时间复杂度是O(1),1次就能定位到,所以效率非常高;
从代码编写量来看:stream是最简洁的,for循环是量最多的;

但是,我最想推荐的遍历方式是:stream

stream().forEach用的多线程方式,其调用线程池的时候必然会耗费更多的时间。但如果你在循环内要处理的事情很多,或者要循环调用远程接口/数据库的时候,无疑极大的提升了效率。
  回顾编程的发展历史,我们不难发现一个规律,那就是先是从最初的C/C++演变到Java/.net,这是编程界的一大进步,因为我们不再关注于指针操作,比如在java中JVM虚拟机已经帮我们完成了相应的操作,由于这一进步,这付出的代价是执行效率会降低,但是带来的好处就在于加快了编程开发的速度。
  当编程由Java/.net演变到JavaScript/PHP/Kotlin,这又是编程界的另一大进步,这意味着我们在编写程序时没有必要再关注于数据类型,而该数据类型是由相应的语言在运行时确定,这样,这又一次降低了程序的运行速度,但是相应的又提升了代码编写的效率,因而通过回顾历史我们不难得出如下结论:
在编写代码时,一定要以最简洁为原则,毕竟运行程序的硬件成本会随着时间的推移在不断降低,而程序员的薪资则不会。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容