for 、foreach 、iterator 三种遍历方式的比较
习惯用法
for、foreach循环、iterator迭代器都是我们常用的一种遍历方式,你可以用它来遍历任何东西:包括数组、集合等
for 惯用法:
List<String> list = new ArrayList<String>();
String[] arr = new String[]{"1,2,3,4"};
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
for(int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
foreach 惯用法:
String[] arr = new String[]{"1,2,3,4"};
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(String str : arr){
System.out.println(str);
}
for (String item : list) {
System.out.println(item);
}
Iterator 惯用法:
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
速度对比
性能是我们选取某一种技术手段的一种考虑方式,且看这三种遍历方式的速度对比
List<Long> list = new ArrayList<Long>();
long maxLoop = 2000000;
for(long i = 0;i < maxLoop;i++){
list.add(i);
}
// for循环
long startTime = System.currentTimeMillis();
for(int i = 0;i < list.size();i++){
;
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");
// foreach 循环
startTime = System.currentTimeMillis();
for(Long lon : list){
;
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");
// iterator 循环
startTime = System.currentTimeMillis();
Iterator<Long> iterator = list.iterator();
while (iterator.hasNext()) {
iterator.next();
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");
4ms
16ms
9ms
由以上得知,for()循环是最快的遍历方式,随后是iterator()迭代器,最后是foreach循环
remove操作三种遍历方式的影响
for循环的remove
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(int i = 0;i < list.size();i++){
if("2".equals(list.get(i))){
System.out.println(list.get(i));
list.remove(list.get(i));
}
}
for循环可以直接进行remove,不会受到任何影响。
foreach 中的remove
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("2".equals(item)) {
System.out.println(item);
list.remove(item);
}
}
你觉得这段代码的正确输出是什么?我们一起来探究一下
当我执行一下这段代码的时候,出现了以下的情况

由以上异常情况的堆栈信息得知,程序出现了并发修改的异常,为什么会这样?我们从错误开始入手,
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
也就是这行代码,找到这行代码的所在地
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
你会好奇, modCount 和 expectedModCount 是什么变量?在我对 ArrayList 相关用法那篇文章中有比较详细的解释。我大致说明一下: modCount 相当于是程序所能够进行修改 ArrayList 结构化的一个变量,怎么理解?看几个代码片段





你能够从中获取什么共性的特征呢?没错,也就是涉及到其中关于ArrayList的 容量大小 和 __元素个数__的时候,就会触发modCount 的值的变化
expectedModCount这个变量又是怎么回事?从ArrayList 源码可知,这个变量是一个局部变量,也就是说每个方法内部都有expectedModCount 和 modCount 的判断机制,进一步来讲,这个变量就是 预期的修改次数,
先抛开这个不谈,我们先来谈论一下foreach(增强for循环)本身。
增强for循环是Java给我们提供的一个语法糖,如果将以上代码编译后的class文件进行反编译(使用jad工具)的话,可以得到以下代码:
terator iterator = item.iterator();
也就是说,其实foreach 每次循环都调用了一次iterator的next()方法
因此才会有这个堆栈信息:
at java.util.ArrayList$Itr.next(ArrayList.java:859)
下面我们来尝试分析一下这段代码报错的原因:
1、第一次 以 “1”的值进入循环,"1" != "2", 执行下一次循环
2、第二次循环以"2"的值进入,判断相等,执行remove()方法(注意这个remove方法并不是 iterator的remove(),而是ArrayList的remove()方法),导致modCount++
3、再次调用next()的时候,modCount != expectedModCount ,所以抛出异常
Iterator迭代器的remove
使用迭代器进行遍历还有很多需要注意的地方:
正确的遍历
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
it.remove();
}
这是一种正确的写法,如果输出语句和 remove()方法互换顺序怎么样呢?
错误的遍历 —— next() 和 remove() 执行顺序的问题
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
it.remove();
System.out.println(it.next());
}
执行程序输出就会报错:
Exception in thread "main" java.lang.IllegalStateException
at java.util.ArrayList$Itr.remove(ArrayList.java:872)
at test.SimpleTest.main(SimpleTest.java:46)
这又是为什么? 还是直接从错误入手:
定位到错误的位置
at java.util.ArrayList$Itr.remove(ArrayList.java:872)

发现如果 lastRet 的值小于 0就会抛出非法状态的异常,这个lastRet是什么?
且看定义:

lastRet的赋值场景

由上面代码可以看出,当你执行next()方法的时候, lastRet 赋值为i,所以这个elementData[]中的下标最小是0,所以这个时候lastRet 最小的值是0, 那么只有当执行remove()方法的时候,lastRet的值赋值为-1,也就是说,你必须先执行一次next方法,再执行一次remove方法,才能够保证程序的正确运行。
**错误的遍历 —— 使用Arrays.asList() **
List<String> list = Arrays.asList("1","2","3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
it.remove();
}
这段代码执行之后的输出是怎样的呢?
1
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(AbstractList.java:161)
at java.util.AbstractList$Itr.remove(AbstractList.java:374)
at test.SimpleTest.main(SimpleTest.java:50)
很不幸,这段代码也抛出了异常,直接从错误处入手发现,这个remove()方法调用的是AbstractList中的remove方法,跟进入发现有一段代码

remove()方法:

也就是说,只要这段代码执行了,都会报错,抛出异常
后记:
上述文章主要介绍了 for循环、foreach 循环、iterator 迭代器遍历元素的速度大小的比较
还介绍了各自遍历过程中 对remove操作的影响。
如果你有什么问题或者好的建议,欢迎你与我一起讨论
for 、foreach 、iterator 三种遍历方式的比较的更多相关文章
- Map三种遍历方式
Map三种遍历方式 package decorator; import java.util.Collection; import java.util.HashMap; import java.util ...
- Java中List集合的三种遍历方式(全网最详)
List集合在Java日常开发中是必不可少的,只要懂得运用各种各样的方法就可以大大提高我们开发的效率,适当活用各种方法才会使我们开发事半功倍. 我总结了三种List集合的遍历方式,下面一一来介绍. 首 ...
- set的三种遍历方式-----不能用for循环遍历(无序)
set的三种遍历方式,set遍历元素 list 遍历元素 http://blog.csdn.net/sunrainamazing/article/details/71577662 set遍历元素 ht ...
- 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法
具体见第三阶段scala-day01中的文档(scala编程基础---基础语法) 1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
- Map的三种遍历方式
对于Map的三种方式遍历 1.keySet() 2.values() 3.entrySet()三种方式得到Set之后,都可以使用 foreach或者iterator, 不能使用for,因为数据结构决定 ...
- java集合的三种遍历方式
import java.util.ArrayList; import java.util.Collection;import java.util.Iterator;public class Home ...
- Java中list对象的三种遍历方式
1.增强for循环 for(String str : list) {//其内部实质上还是调用了迭代器遍历方式,这种循环方式还有其他限制,不建议使用. System.out.println(str); ...
- java 测试:iterator foreach for 三种迭代方式哪种更快?
代码: public class main { public static void main(String[] p_args){ ArrayList<String> _l_string ...
随机推荐
- stm32之PWM学习
下图是一个STM32普通PWM形成的图形原理说明 自动重装载寄存器(ARR)用于确定波形的频率(即周期).捕获比较寄存器(CCRx)(用于确定占空比的) PWM的工作过程如下:首先ARR寄存器里面的值 ...
- 线段树:HDU2795-Billboard(建树方式比较新奇)
Billboard Time Limit: 20000/8000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Su ...
- Java观察者模式(Observer)
一.定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己.主要应用在java的AWT事件机制 ...
- splay模板三合一 luogu2042 [NOI2005]维护数列/bzoj1500 [NOI2005]维修数列 | poj3580 SuperMemo | luogu3391 【模板】文艺平衡树(Splay)
先是维修数列 题解看这里,但是我写的跑得很慢 #include <iostream> #include <cstdio> using namespace std; int n, ...
- Linux进程间通信(IPC)
linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间 ...
- custom post types 404 Page Error
问题: 注册新的文章类型后,用新的类型写文章,打开后报 404 错误 原因: 因为虽然注册了新的帖子类型,但WordPress还不知道如何处理它 解决: 到设置 -> 固定链接,重新点击保存,再 ...
- Get Sauce(状压DP)
描述 In order to celebrate the 8th anniversary of ZOJ, LCLL goes to a sauce factory to "Get Sauce ...
- API经济时代的思考(转载目的:为之后写API-first模式的生命周期治理做准备)
原文地址:API经济时代的思考 感觉这篇博客还不错,个人赞同其大部分的内容,借鉴参考一下,懒得自己写了(关键是不一定能轻松写得更好,嘿嘿,偷懒啦) 接下来会写关于API经济的概念下,如何进行AP ...
- 2016 年 ACM/ICPC 青岛区域赛 Problem C Pocky
昨晚乱入学弟的训练赛,想了一下这个题.推导的过程中,加深了对公理化的概率论理解.$\newcommand{\d}{\mathop{}\!\mathrm{d}}$ 解法一 考虑 $ d < L$ ...
- Master&Slave是什么?
由于前段时间公司项目比较赶,一直抽不出时间写博客,今天偷空写一篇吧.前面给大家讲解了单机版redis的基本操作,现在继续给大家讲解一下Redis的进阶部分,主从复制和读写分离. 一.Master&am ...