在工作中经常写代码时会遇到遍历list并且过滤的一些逻辑,现在来看一下不同的遍历的优劣:

case在50000的范围内过滤3和7的倍数

先看一个案例

这个是以7为倍数,看结果大家可能以为过滤没问题,大家其实别被表象误导,其实遍历到49的时候就会出现错误(7*7=49),只是此时可能没看出来,来一个比较明显的:加一个条件再过滤3的呢。

可以看到7为倍数的数字没被过滤,而这个就是直接循环使用remove容易出现的bug,真正原因看下内部的remove方法:

   public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}
    /** Private remove method that skips bounds checking and does not* return the value removed.*/private void fastRemove(int index) {modCount++;int numMoved = size - index - 1;if (numMoved > 0)//remove会导致之后的元素往前移动,而下标不改变时就会出现bugSystem.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its work}

1.正确的做法应该是这样:

耗时:200ms左右

2.再看一下foreach遍历的:

报了一个错误异常,而想了解其中真正的原因需要知道foreach的内部实现,foreach其实是用迭代器来进行遍历的,而在遍历时直接使用arraylist的remove方法会导致什么问题呢?

可以再看一下fastremove和迭代器遍历的内部代码:

最后导致抛出上面异常的其实就是这个

        final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}//调用next时会调用checkForComodification方法检查 这两个字段//而fastRemove里面只对modCount 进行了改变 导致抛出异常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;return (E) elementData[lastRet = i];}

所以遍历时remove并不适用于foreach,接下来再看一下迭代器的效果

3.

耗时:150ms左右

使用迭代器的好处呢就是不用关注下标的变化和内部的实现,本来这也就是责任链设计模式想达到的效果,并且可以看到效果也比普通遍历要稍好一点,当然目前的list的遍历可不止这些,继续往下探索:

4.使用复制list

耗时:60ms左右

可以看到这次的效率就比较突出了,而只要的原因呢?

其实是因为以ArrayList为例的内部remove方法(前面有fastRemove代码就不贴了)也肯定是会比较消耗系统资源的,而直接将符合条件的元素add到其他的list中肯定效率是会更加高效的

接下来进入JDK8的时代,其中遍历list的花样也就增加了

5.java8的新方法removeIf()

耗时:150ms左右

和迭代器的效率差不多,看下内部实现其实就知道了:

    //内部其实就是迭代器遍历default boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);boolean removed = false;final Iterator<E> each = iterator();while (each.hasNext()) {if (filter.test(each.next())) {each.remove();removed = true;}}return removed;}

6.Stream:

使用java8中的stream来进行list过滤:

耗时:150ms左右

以上都是以ArrayList为例,现在来看一下LinkList的不同效果呢:

7.LinkList的复制

耗时:150ms左右 

8. LinkList的迭代器:

耗时:150ms左右

之前效果突出的复制list的做法在LinkList上却感觉没什么效果了?  知道LinkList的原理想清楚也就不难了,LinkList内部是一个链表,而链表中的添加和删除节点都是O(1)的复杂度,但是缺点是遍历查找某一个元素的时间复杂度为O(n)就比较高了

从以上初步测试中可以得到以下几个结论

1----在不考虑内存大小会不会出现OOM的时候,采取复制一个满足条件的新list速度更快,适用于集合中的对象不算多时,毕竟只需要add操作,并且ArrayList的速度大于Linklist,因为链表在循环遍历时是很慢的

.

2----当集合中的元素过多过大时,第一种方案就不行,但是现实场景中比较少见,采用迭代器的方式进行遍历是较快的,并且也不用专注下标的变化,ArrayList和LinkList的速度也差不多

3----不考虑性能使用removeIf方法代码更加简洁明了

3----在forEach遍历时不能使用list.remove()方法,否则会抛ConcurrentModificationException异常

4----链表无论是使用复制还是迭代器进行删除元素速度都差不多

因此究竟在采用那种list集合或者方式进行元素遍历时的删除时,都需要根据不同的场景来定

List遍历remove的那些事相关推荐

  1. vector/list/map/set的插入、删除、遍历 - remove\erase函数

    1.vector中删除满足某些条件的元素和迭代器失效问题 #include <iostream> #include <vector> using namespace std; ...

  2. stl的set,multiset, map, multimap, deque, list, stack, queue, priority_queue

    set实际上是平衡二叉树,需要声明头文件#include<set> Insert:将元素插入集合中 使用前向迭代器对集合中序遍历 使用反向迭代器reverse_iterator可以反向遍历 ...

  3. 动图 + 源码,演示 Java 中常用数据结构执行过程及原理

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList Linke ...

  4. 几张动态图捋清Java常用数据结构及其设计原理

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList Linke ...

  5. 指定jdk8_动图+源码+总结:深度解析 JDK8 中的数据结构(珍藏版)

    最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程 文末,有个特别好的网站推荐 主要基于JDK8, 可能会有些特性与jdk7之前不相同, 例如Lin ...

  6. 基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#)

    基于虎书实现LALR(1)分析并生成GLSL编译器前端代码(C#) 为了完美解析GLSL源码,获取其中的信息(都有哪些in/out/uniform等),我决定做个GLSL编译器的前端(以后简称编译器或 ...

  7. 数据结构中缀表达式转后缀表达式与后缀表达式的求值实训报告_动图+源码,演示 Java 中常用数据结构执行过程及原理...

    程序员的成长之路互联网/程序员/成长/职场 关注 阅读本文大概需要 3.7 分钟. 作者:大道方圆cnblogs.com/xdecode/p/9321848.html 最近在整理数据结构方面的知识, ...

  8. 剑指offer.机器人的运动范围

    地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼∼n−1.一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格.但是不能进入行坐标和列坐标的数位 ...

  9. 图解 Java 常用数据结构

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过 ...

最新文章

  1. iOS逆向(4)-代码注入,非越狱窃取微信密码
  2. python从外部传入参数_从另一个Python脚本运行一个Python脚本,并传入参数 - python...
  3. python入门与提高实践,Python基础06:功能增强与实践,基础知识,学习,函数,加强,及,练习...
  4. C语言数组作为传入参数
  5. java ftp 读取 txt文件_java通过ftp方式读取文件,并解析入库
  6. SQL Server 语句查询手册
  7. Avalonia跨平台入门第八篇之控件的拖放
  8. web.xml 组件加载顺序
  9. Postman使用小教程--基础入门篇
  10. oracle提交数据按键,Oracle PLSQL - 仅提交数据库链接(Oracle PLSQL - Commit only database link)...
  11. mysql的rowscn_Oracle ORA_ROWSCN 伪列 说明
  12. Ubuntu 10不能通过改source.list装JDK 1.6
  13. poj 1001 分析
  14. vue-cli3开干
  15. 服务器 iis ftp配置文件,如何:在 IIS 中创建和配置 FTP 站点
  16. [Unity]Unity3D游戏引擎游戏开发软件相比与其他的优势
  17. 情人节送什么礼物给女友比较好、这几款就够了
  18. php 搜索引擎 分词_PHP 实现中文分词搜索功能
  19. Android手账本案例
  20. android 全选功能,Android实现ListView控件的多选和全选功能实例

热门文章

  1. java word 乱码_JAVA 使用POI替换word中的某些字符串,在本地调试一切ok,在服务器上出现乱码...
  2. sem_timedwait和pthread_cond_timedwait、pthread_mutex_timedlock()
  3. 后端开发必备——Nginx篇
  4. 高性能的“高”,从何而来?今天来谈一谈高性能服务器
  5. 什么技术水平,才能拿到腾讯T9(原T3.1)offer,“8+1”的技术维度总结
  6. mysql内存工作机制_MySQL内存使用机制
  7. 中国联通-中国联通6G白皮书——附下载链接
  8. 一个月python训练计划
  9. mjlt2型号是a1398吗_怎么区分MacBook Pro的型号
  10. CSDN周赛第30期题目解析(天然气定单、小艺读书、买苹果、圆桌)