初探Lambda表达式/Java多核编程【0】从外部迭代到内部迭代
开篇
放假前从学校图书馆中借来一本书,Oracle官方的《精通Lambda表达式:Java多核编程》。
假期已过大半才想起来还没翻上几页,在此先推荐给大家。
此书内容及其简洁干练,如果你对Java语法有基础的认识看起来就会不费劲,唯一的缺点就是代码部分的内容以及排版有些错误,不过瑕不掩瑜,无伤大雅。
这个系列就是我对书中每一小节的一个提炼、总结以及实践,每一个知识点我都会附上自己写的代码,这些代码用来验证所学的知识。
才疏学浅,如果有理解错误之处请指正,欢迎交流讨论。
遍历一个集合
最传统的方法大概是用Iterator,当然我比较Low,习惯用i<arr.size()
这类的循环。(现在我用for/in,本质上还是Iterator...)
这一类方法叫做外部迭代,意为显式地进行迭代操作,即集合中的元素访问是由一个处于集合外部的东西来控制的,在这里控制着循环的东西就是迭代器。
书中举的例子是pointList,我在这里把它换成一个电话簿。
public class ContactList extends ArrayList<String>{}
里面存储着String类型的联系人。
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
System.out.println(contactListIterator.next());
}
现在我们将这种遍历方式换成内部迭代。
顾名思义,这种方式的遍历将在集合内部进行,我们不会显式地去控制这个循环。
无需关心遍历元素地顺序,我们只需要定义对其中每一个元素进行什么样的操作。注意在这种设定下可能无法直接获取到当前元素的下标。
Java5中引入Collection.forEach(...)
,对集合中每一个元素应用其行为参数,这个方法是从其父接口Iterable中继承。
现在我们可以去重写forEach方法。
@Override
public void forEach() {
for(String s : this) {
System.out.println(s + " is your contact.");
}
}
这下我们对电话簿调用forEach方法的时候,遍历操作就会在类的内部完成了。
当然这看起来非常傻并且很不灵活。如果我们想把行为作为参数传给forEach呢?
所幸Java8提供了这种可能,这种行为参数叫做Consumer。
public interface Consumer<T> {
void accept(T t);
}
一个明显的函数式接口,行为定义在accept方法中。
在定义好了我们自己的Consumer之后,现在这样写:
@Override
public void forEach(Consumer<String> c) {
for(String s : this) {
c.accept(s);
}
}
傻的程度减轻了一些,当然还是不够机智。
在这种情况下一个匿名内部类就能搞定问题(Android里面监听器写到手抽...)
contactList.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s + " is your contact.");
}
});
这下足够简洁,当然聪明的编译器应该能够推断出我们传入forEach方法的只能是一个Consumer并且需要调用的就是Consumer的唯一方法accept,这段代码还有简化的余地。
所以现在需要登场的就是Lambda表达式了。
既然我们的电话簿ContactList本质上是一个ArrayList<String>
,那么编译器也一定能推断出Consumer的类型参数标识为String。
所以这下连参数类型都省了。
contactList.forEach(s -> System.out.println(s + " is your contact, again!"));
->前的p为参数名(即String s
中的s),->后为方法体,也可以用一个大括号括起来,因为我这里只写了一句所以就没用。
这就是内部迭代和Lambda相结合的终极奥义了。
现在附上测试代码:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Consumer;
public class ContactList extends ArrayList<String> {
@Override
public void forEach(Consumer<? super String> action) {
super.forEach(action);
}
public static void main(String[] args) {
ContactList contactList = new ContactList();
contactList.add("Foo");
contactList.add("Bar");
contactList.add("Nico");
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
System.out.println(contactListIterator.next());
}
System.out.println("\n--- Consumer is coming! ---\n");
contactList.forEach(new ContactAction());
System.out.println("\n--- Lambda is coming! ---\n");
contactList.forEach(s -> System.out.println(s + " is your contact, again!"));
}
static class ContactAction implements Consumer<String> {
@Override
public void accept(String s) {
System.out.println(s + " is your contact.");
}
}
}
以及运行结果:
Foo
Bar
Nico
--- Consumer is coming! ---
Foo is your contact.
Bar is your contact.
Nico is your contact.
--- Lambda is coming! ---
Foo is your contact, again!
Bar is your contact, again!
Nico is your contact, again!
初探Lambda表达式/Java多核编程【0】从外部迭代到内部迭代的更多相关文章
- 初探Lambda表达式/Java多核编程【1】从集合到流
从集合到流 接上一小节初探Lambda表达式/Java多核编程[0]从外部迭代到内部迭代,本小节将着手使用"流"这一概念进行"迭代"操作. 首先何为" ...
- 初探Lambda表达式/Java多核编程【2】并行与组合行为
今天又翻了一下书的目录,第一章在这之后就结束了.也就是说,这本书所涉及到的新的知识已经全部点到了. 书的其余部分就是对这几个概念做一些基础知识的补充以及更深层次的实践. 最后两个小节的内容较少,所以合 ...
- 初探Lambda表达式/Java多核编程【3】Lambda语法与作用域
接上一篇:初探Lambda表达式/Java多核编程[2]并行与组合行为 本节是第二章开篇,前一章已经浅显地将所有新概念点到,书中剩下的部分将对这些概念做一个基础知识的补充与深入探讨实践. 本章将介绍L ...
- 初探Lambda表达式/Java多核编程【4】Lambda变量捕获
这周开学,上了两天感觉课好多,学校现在还停水,宿舍网络也还没通,简直爆炸,感觉能静下心看书的时间越来越少了...寒假还有些看过书之后的存货,现在写一点发出来.加上假期两个月左右都过去了书才看了1/7都 ...
- Lambda&Java多核编程-5-函数式接口与function包
从前面的总结中我们知道Lambda的使用场景是实现一个函数式接口,那么本篇就将阐述一下何为函数式接口以及Java的function包中提供的几种函数原型. 函数式接口 早期也叫作SAM(Single ...
- Lambda&Java多核编程-6-方法与构造器引用
在Lambda&Java多核编程-2-并行与组合行为一文中,我们对Stream<Contact>里的每一位联系人调用call()方法,并根据能否打通的返回结果过滤掉已经失效的项. ...
- Lambda表达式和函数式编程
Lambda表达式和函数式编程 https://www.cnblogs.com/bigbigbigo/p/8422579.html https://www.runoob.com/java/java8- ...
- Lambda&Java多核编程-7-类型检查
本篇主要介绍Lambda的类型检查机制以及周边的一些知识. 类型检查 在前面的实践中,我们发现表达式的类型能够被上下文所推断.即使同一个表达式,在不同的语境下也能够被推断成不同类型. 这几天在码一个安 ...
- Lambda 表达式(C# 编程指南) 微软microsoft官方说明
Visual Studio 2013 其他版本 Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数. 通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地 ...
随机推荐
- xmlns=""
用dom4j把两个手上的element撮合到一起,dom4j就会在被加的元素上写上xmlns="",当时我也没在意,后来用户要求去掉,查了一下还不是那么容易,原来xml本身对nam ...
- jpg转bmp(使用libjpeg)
源: jpg转bmp(使用libjpeg) [转]JPEG压缩原理 bmp转jpg(使用libjpeg)
- AngularJs ng-class 使用
今天在做项目的时候要对表格内的部分的最大最小值高亮 解决方案 1. 引用 ng-class 2. 引用原型求最大最小值 实例 AngularJs HTML 代码 <table class=&qu ...
- MySQL协议分析2
MySQL协议分析 议程 协议头 协议类型 网络协议相关函数 NET缓冲 VIO缓冲 MySQL API 协议头 ● 数据变成在网络里传输的数据,需要额外的在头部添加4 个字节的包头. . packe ...
- pcommlite串口通讯库使用
MFC下串口编程使用最多的两种方法是读取注册表和使用mscomm组件,都有着或多或少的缺陷,调用系统SDK比较麻烦,而MSCOMm组件最多支持16个串口,串口号大于16的时候无法打开,遇到这种情况,可 ...
- HMC5883L地磁传感器驱动
霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域.HMC5883L 包括最先进的高分辨率 HMC118X 系列磁阻传感器,并附带霍 ...
- [MySQL]mysql指定路径启动
/usr/sbin/mysqld --defaults-file=/etc/mysql/my.cnf --basedir=/usr --datadir=/var/lib/mysql --pid-fil ...
- [git] 细说commit (git add/commit/diff/rm/reset 以及 index 的概念)
http://kasicass.blog.163.com/blog/static/39561920133294219374/ 创建测试仓库 $ git init $ echo "line o ...
- 【腾讯优测干货分享】微信小程序之自动化亲密接触
本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/HcPakz5CV1SHnu-U8n85pw 导语 山雨欲来风满楼,最 ...
- OpenCV点滴2
明天回家了-- 昨天去见了老师,去之前告诉自己不要紧张,去了后还是紧张了,语无伦次,很没礼貌--(不敢看老师的眼睛) 刚才妈打电话说让我早点回去,不知道为什么,在空荡荡的宿舍里听到妈的声音,眼泪会夺眶 ...