对于我们常用的ArrayList等容器类,经常需要一个一个遍历里面的元素,从而对各个元素执行对应的操作。

  像我代码写多了,通常的做法是用传统的,类似于数组遍历的方法,即在for循环中设置一个int变量作为索引,然后用List的get方法,想怎么做就怎么做,不会遇到任何不能做的事。

  当然,偶尔我会写得简单一点,用 for (元素类型 变量名 :集合) 的方法,即不用索引,直接指定实际的元素类型,取下一个元素,这样可以少写一行代码。

  但不管用哪一种,我都没有考虑过用迭代器iterator,虽然教程上面经常提到这东西,我也知道这是用来遍历,顺便执行删除等操作的。因为用类似数组遍历的方法取元素从未遇到过瓶颈,就从来没研究过iterator,也一直都觉得这是个没有卵用的东西。

  最近在研究ArrayList和LinkedList源码的时候,源码里面也有很大一段是关于Iterator的,这让我更加不解了:既然是一个可有可无可替代的东西,为什么官方还要费这么大的劲来描述它呢?

  直到最近阅读《Effective Java》,看了一节关于for-each和传统for循环的比较,里面有一句话让我重新审视Iterator:

    Not only does the for-each loop let you iterate over collections and arrays,

    it lets you iterate over any object that implements the Iterable interface.

    这就不得不让我怀疑:难道我以前所知道的集合类,就是因为实现了Iterable接口,才可以用for加冒号的方式?

  

  google了一下,果然,for-each这样一种简洁的书写方式,内部居然就是用迭代器实现的!

  下面的代码摘自StackOverFlow  

  

List<String> someList = new ArrayList<String>();

  正常的用for-each遍历方法:

for (String item : someList) {
System.out.println(item);
}

  重点来了,上面这简单的一句话,在编译过程中,被编译器自动翻译成了下面这段,真正执行的时候也正是下面这段:

for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
String item = i.next();
System.out.println(item);
}

  

  所以,在实际开发中,虽然明面上很少用到标标准准的Iterator,但是常用的for-each的暗箱操作却都是Iterator!

  Java创造出来for-each的操作,好处之一当然是简化了代码的书写,减少了变量个数。

  另外还有很重要的一点是大大降低了遍历过程当中的误操作。想想看,如果在for-each过程中,你得到了当前位置的元素值,有什么办法可以添加、删除或修改元素值呢?答案是没有。然而利用Iterator或者索引来写循环,你可以进行几乎所有的增删改操作。所以利用for-each来操作更安全。

  当然,for-each还有一种用法,即用在普通数组的遍历当中,当中也进行了暗箱操作,即转换为索引的遍历。

int[] test = new int[] {1,4,5,7};

for (int intValue : test) {
// do some work here on intValue
}

  

  编译时转换为:

int[] test = new int[] {1,4,5,7};

for (int i = 0; i < test.length; i++) {
int intValue = test[i];
// do some work here on intValue
}

一定要注意,for-each只能用于:①Iterable ②数组

也即,除了数组以外,一般的类只要实现了Iterable接口就能用for-each

我们可以随便写个类玩玩。

import java.util.Iterator;

public class MyClass<E> implements Iterable<E>{

    private class MyIterator implements Iterator<E> {

        private int max = 10;
private int cur = 0; @Override
public boolean hasNext() {
if (cur < max)
return true;
else
return false;
} @SuppressWarnings("unchecked")
@Override
public E next() {
Object res = ++cur;
return (E) res;
} @Override
public void remove() {
System.out.println("索引:"+cur+" 被删除");
} }; @Override
public Iterator<E> iterator() {
return new MyIterator();
} }

测试下

import java.util.Iterator;

public class JavaMain {

    public static void main(String[] args) {
MyClass<Integer> m = new MyClass<>();
for (Integer i : m) {
System.out.println(i);
}
System.out.println("-------------------------------------------");
for (Iterator<Integer> it = m.iterator();it.hasNext();) {
Integer val = it.next();
if (val % 3 == 0)
it.remove();
}
} }

输出结果:

1
2
3
4
5
6
7
8
9
10
-------------------------------------------
索引:3 被删除
索引:6 被删除
索引:9 被删除

我们这里的MyClass类没有任何实际的意义,居然试验成功了,真是一件不可思议的事情。。。

参考资料:

http://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work

http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2

Java暗箱操作之for-each的更多相关文章

  1. Java暗箱操作之enum

    enum,即枚举类型,在每种编程语言中都有类似的类型. 因为用得少,语法规则很难记得住,我每次看到enum都会感到害怕. 一般的enum语法是这样的: public class MyClass { p ...

  2. Java暗箱操作之自动装箱与拆箱

    我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.a ...

  3. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  4. 给Java开发人员的Play Framework(2.4)介绍 Part1:Play的优缺点以及适用场景

    1. 关于这篇系列 这篇系列不是Play框架的Hello World,由于这样的文章网上已经有非常多. 这篇系列会首先结合实际代码介绍Play的特点以及适用场景.然后会有几篇文章介绍Play与Spri ...

  5. Java虚拟机的内存管理

    众所周知,Java程序员写的代码是没有办法控制Java对象的内存释放的,完全有JVM暗箱操作. 虽然程序员把内存的释放的任务都交给了Java虚拟机,但是并不代表Java程序就不存在内存泄漏. 反而,某 ...

  6. 【Java技术系列】爱情36技之追美妹的技术

    1. 在古老的非洲大陆上,有个原始人无意中抬头仰望星空,凝视的时间稍微长了一些,超过了外星人设置的阈值,立刻拉响了人类即将产生文明的警报.因为外星人认为,人类已经产生了对宇宙的好奇心,文明的产生,科技 ...

  7. 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题

    背景起因: 记起以前的另一次也是关于内存的调优分享下   有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...

  8. Elasticsearch之java的基本操作一

    摘要   接触ElasticSearch已经有一段了.在这期间,遇到很多问题,但在最后自己的不断探索下解决了这些问题.看到网上或多或少的都有一些介绍ElasticSearch相关知识的文档,但个人觉得 ...

  9. 论:开发者信仰之“天下IT是一家“(Java .NET篇)

    比尔盖茨公认的IT界领军人物,打造了辉煌一时的PC时代. 2008年,史蒂夫鲍尔默接替了盖茨的工作,成为微软公司的总裁. 2013年他与微软做了最后的道别. 2013年以后,我才真正看到了微软的变化. ...

随机推荐

  1. c# 我所理解的 值类型 and 引用类型

    一直以来对于值类型和引用类型都只是一个模糊的概念,趁最近有空深入理解了下. 先说说值类型,在msdn上是这样介绍值类型的. 意思就是值类型直接包含值. 变量引用的位置就是值所在内存中实际存储的位置,所 ...

  2. 回忆:#define的用法

    ANSI C规定:#前可以有空格或者tab,#和指令其余部分之间也可以有空格,可以出现在任何地方,作用域从定义处到文件结尾. 因为预处理开始前,系统会删除反斜线和换行符的组合,故可以把指令扩展到几个物 ...

  3. RabbitMQ原理与相关操作(三)消息持久化

    现在聊一下RabbitMQ消息持久化: 问题及方案描述 1.当有多个消费者同时收取消息,且每个消费者在接收消息的同时,还要处理其它的事情,且会消耗很长的时间.在此过程中可能会出现一些意外,比如消息接收 ...

  4. ASP.NET MVC 使用 FluentScheduler 定时器计划任务

    MacBook Pro 只有四个 USB Type-C 接口是否错了? 一项新技术的诞生总会对已存在的事物造成冲击或影响,如果大家都害怕冲击与影响,那这个世界永远像现在不变就行了,大家都好好的,待在自 ...

  5. .NET Task揭秘(一)

    Task为.NET提供了基于任务的异步模式,它不是线程,它运行在线程池的线程上.本着开源的精神, 本文以解读基于.NET4.5 Task源码的方式来揭秘Task的实现原理.   Task的创建 Tas ...

  6. C# Enum,Int,String的互相转换 枚举转换

    Enum为枚举提供基类,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32.编程语言通常提供语法来声明由一组已命名的常数和它们的值组成的枚举. 注意:枚举类型的基 ...

  7. 微信扫码支付~官方DEMO的坑~参数不能自定义

    返回目录 由于微信在校验参数时采用了“微信服务端”校验,它的参数是前期定义好的,所以用户不能自己添加自定义的参数,你可以把参数写在Attach字段时,作为它的附加参数. 参数和返回值定义如下: pub ...

  8. this上下文,以及通过call 、apply 实现继承

    上下文:this关键字通常指向当前函数的拥有者,把拥有者叫做执行上下文. this代表函数运行时自动生成的内部对象,只能在函数内部使用. 构造函数中的this 指 构造函数的实例对象.javascri ...

  9. win7下 VirtualBox虚拟机开机后台自启动

    win7下安装个linux虚拟机,学习下非常好. 但是每次使用linux的时候,都是打开virtualBox-->启动安装的linux系统-->再用远程桌面(SSH等)连接 每次手动打开比 ...

  10. 高级Bash Scripting系列笔记--01之“什么情况不适用Bash Script”

      1. 占用资源的任务,尤其那些影响速度的工作 比如排序,哈希,递归等等. 2. 大量使用数学运算 尤其是浮点运算,比如任意精度的计算或者复数计算等等,这类使用C++会好很多. 3. 跨平台的(适用 ...