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
Java暗箱操作之for-each的更多相关文章
- Java暗箱操作之enum
enum,即枚举类型,在每种编程语言中都有类似的类型. 因为用得少,语法规则很难记得住,我每次看到enum都会感到害怕. 一般的enum语法是这样的: public class MyClass { p ...
- Java暗箱操作之自动装箱与拆箱
我以前在写Android项目的时候,估计写得最多最熟练的几句话就是: List<Integer> list = new ArrayList<Integer>(); list.a ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- 给Java开发人员的Play Framework(2.4)介绍 Part1:Play的优缺点以及适用场景
1. 关于这篇系列 这篇系列不是Play框架的Hello World,由于这样的文章网上已经有非常多. 这篇系列会首先结合实际代码介绍Play的特点以及适用场景.然后会有几篇文章介绍Play与Spri ...
- Java虚拟机的内存管理
众所周知,Java程序员写的代码是没有办法控制Java对象的内存释放的,完全有JVM暗箱操作. 虽然程序员把内存的释放的任务都交给了Java虚拟机,但是并不代表Java程序就不存在内存泄漏. 反而,某 ...
- 【Java技术系列】爱情36技之追美妹的技术
1. 在古老的非洲大陆上,有个原始人无意中抬头仰望星空,凝视的时间稍微长了一些,超过了外星人设置的阈值,立刻拉响了人类即将产生文明的警报.因为外星人认为,人类已经产生了对宇宙的好奇心,文明的产生,科技 ...
- 故障重现(内存篇2),JAVA内存不足导致频繁回收和swap引起的性能问题
背景起因: 记起以前的另一次也是关于内存的调优分享下 有个系统平时运行非常稳定运行(没经历过大并发考验),然而在一次活动后,人数并发一上来后,系统开始卡. 我按经验开始调优,在每个关键步骤的加入如 ...
- Elasticsearch之java的基本操作一
摘要 接触ElasticSearch已经有一段了.在这期间,遇到很多问题,但在最后自己的不断探索下解决了这些问题.看到网上或多或少的都有一些介绍ElasticSearch相关知识的文档,但个人觉得 ...
- 论:开发者信仰之“天下IT是一家“(Java .NET篇)
比尔盖茨公认的IT界领军人物,打造了辉煌一时的PC时代. 2008年,史蒂夫鲍尔默接替了盖茨的工作,成为微软公司的总裁. 2013年他与微软做了最后的道别. 2013年以后,我才真正看到了微软的变化. ...
随机推荐
- geotrellis使用(十五)使用Bokeh进行栅格数据可视化统计
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 实现方案 总结 一.前言 之前有篇文章 ...
- php使用post方式获得文件扩展名
<form action="" method="post"> <input type="file" value=" ...
- 让自己也能使用Canvas
<canvas> 是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形.例如:画图,合成照片,创建动画甚至实时视频处理与渲染. 兼容性方面,除了一些骨灰级浏览器IE6.IE ...
- PowerPoint基础
一.基础 默认后缀ppt,pptx office2003和以后的版本只支持ppt, 可以将pptx另存为ppt97-2003 二.修改PPT尺寸 三.新建幻灯片 四.字体与段落设置 五.主题与字体 六 ...
- ZOJ Problem Set - 1001 A + B Problem
ZOJ ACM题集,编译环境VC6.0 #include <stdio.h> int main() { int a,b; while(scanf("%d%d",& ...
- Oracle管理磁盘空间和资源
1.可恢复的空间分配 2.可移动表空间 3.Oracle段收缩功能 4.Oracle数据库资源管理 Reference 1.可恢复的空间分配 1.1 了解可恢复的空间分配 一般情况,我们发出一个大型数 ...
- Mongo查询
这里主要是讲MongoDB在控制台中如何进行高级查询. 还有一句想提醒大家,多动手实验,才是硬道理. <,>,>=,<= 这四个就不用解释了,最常用的,也是最简单的. db.c ...
- IO多路复用之poll总结
1.基本知识 poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制.poll和selec ...
- Ubuntu实现树莓派交叉编译
一.交叉编译 在一个平台上生成另一个平台上的可执行代码.为什么要大费周折的进行交叉编译呢?一句话:不得已而为之.有时是因为目的平台上不允许或不能够安装所需要的编译器,而又需要这个编译器的某些特征:有时 ...
- CClayer ignoreAnchorPointForPosition 参数的作用
ignoreAnchorPointForPosition:忽略锚点对于位置的设定.即非位置的设定(比如说缩放),则不受此参数的控制. 并且默认情况下CCLayer的默认锚点是 中点,而不是左下角的点. ...