Java暗箱操作之for-each

  对于我们常用的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

时间: 2024-10-30 04:11:54

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

Java暗箱操作之enum

enum,即枚举类型,在每种编程语言中都有类似的类型. 因为用得少,我每次看到enum都会望而生畏,因为它的语法实在是“不按常规出牌”啊! 一般的enum语法是这样的: public class MyClass { private enum Fruit {APPLE, ORANGE, GRAPE, BANANA} //类型定义 private Fruit fruit = Fruit.APPLE; //类型使用 private void useEnum() { if (fruit == Fruit

Java语言暗箱操作之自动装箱与拆箱

我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.add(1); //把一个整数加入到集合中 int i = list.get(0); //从集合中取出元素 ArrayList用起来是多么的顺手!当时我只知道尖括号<>里面只能加入大写字母开头的Object类型,不能加入int.char.double这些原始类型,至于原因没研究过,这么规定就这么用呗

Java虚拟机的内存管理

众所周知,Java程序员写的代码是没有办法控制Java对象的内存释放的,完全有JVM暗箱操作. 虽然程序员把内存的释放的任务都交给了Java虚拟机,但是并不代表Java程序就不存在内存泄漏. 反而,某程度上,当出现内存泄漏,Debug会变得难度更大. 所以,Java程序员,有必要去了解Java虚拟机对于内存的管理以及垃圾回收的机制. Java虚拟机是如何判断一个对象可以回收? 当一个对象没有被任何其他所引用时,这个对象被Java虚拟机视为可回收. 早起的虚拟机,使用引用计数的方法判断对象是否可回

java 十宗罪

非常抱歉,第1点关于"abc"=="abc"返回false是我搞错了,在java里是返回true的,我没有经过实验就这么说了,非常抱歉.之所以会这么说,是因为不记得是哪一种,因为我使用了==进行了字符串的比较,结果并没有返回我相要的结果,然后请教老人,老人们就训诫道:最好使用equals进行比较,而不要使用==,故有此感慨. 好吧,我知道看这标题很多人就忍不住要拍砖或表示不赞同了,我都接受. 我在遇到问题时,找一些搞java朋友求助,有能解决的,我想说的是不能解决的

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

1. 关于这篇系列 这篇系列不是Play框架的Hello World,由于这样的文章网上已经有非常多. 这篇系列会首先结合实际代码介绍Play的特点以及适用场景.然后会有几篇文章介绍Play与Spring,JPA(Hibernate)的集成,以及一些Play应用的最佳实践. 这期间会在Github上提供一个脚手架项目.方便感兴趣的朋友直接动手尝试. 最后会简单分析Play的部分源码.帮助大家理解黑盒子的内部机制. 我水平有限,有错误欢迎指出. 2. Play介绍 Play Framework是一

JAVA基础学习之-AQS的实现原理分析

AbstractQueuedSynchronizer是JUC的核心框架,其设计非常精妙. 使用了Java的模板方法模式. 首先试图还原一下其使用场景:对于排他锁,在同一时刻,N个线程只有1个线程能获取到锁:其他没有获取到锁的线程被挂起放置在队列中,待获取锁的线程释放锁后,再唤醒队列中的线程. 线程的挂起是获取锁失败时调用Unsafe.park()方法:线程的唤醒是由其他线程释放锁时调用Unsafe.unpark()实现.由于获取锁,执行锁内代码逻辑,释放锁整个流程可能只需要耗费几毫秒,所以很难对

Java多线程学习(吐血超详细总结)

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程

Java TM 已被阻止,因为它已过时需要更新的解决方法

公司的堡垒机需要通过浏览器登陆,且该堡垒机的网站需要Java的支持,最近通过浏览器登陆之后总是提示"java TM 已被阻止,因为它已过时需要更新的解决方法"导致登陆之后不能操作, 但是操作系统中确实已经安装了比较新的JDK,安装的JDK版本是jdk-7u67-windows-i586,因为太烦人,所以决定搞清楚报错的原因,一劳永逸,彻底解决这个问题 准备工作:安装JDK,安装版本jdk-7u67-windows-i586.exe,因为机器的Eclipse还依赖64位的JDK,所以另安

Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? Java new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 1 2 3 4 5 6 7 new Thread(new