初探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 表达式,可以写入可作为参数传递或作为函数调用值返回的本地 ...
随机推荐
- Hibernate---基础配置
hibernate.cfg.xml里可以设置一个值显示更详细的sql语句: <property name="format_sql">true</property& ...
- mysql 隔离级别与锁
1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约 ...
- STM32标准IIC驱动
IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接 微控制器及其外围设备.也是目前很流行的通讯总线,使用IIC总线做产品能够很大 ...
- APP被苹果APPStore拒绝的各种原因
APP被苹果APPStore拒绝的各种原因 1.程序有重大bug,程序不能启动,或者中途退出.2.绕过苹果的付费渠道,我们之前游戏里的用兑换码兑换金币.3.游戏里有实物奖励的话,一定要说清楚,奖励由本 ...
- PHP内核变量存储
PHP作为一门弱类型语言,其变量类型可任意改变.而C作为PHP的底层实现,是通过结构及联合来实现PHP变量的弱类型特性的.在PHP源码中,Zend/zend.h文件有关于PHP变量的结构定义. 01 ...
- MAC上更攺jenkins默认安装目录
/Library/LaunchDaemons #编缉里面的jenkinshome和username sudo vim org.jenkins-ci.plist #然后 sudo launchctl u ...
- JavaScript(一)---- 概述
JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...
- UVa 10427 - Naughty Sleepy Boys
题目大意:从1开始往后写数字,构成一个如下的字符串 123456789101112... .求第n位的数字是多少. 找规律,按数字的位数可以构建一个类似杨辉三角的东西,求出第n位是哪个数的第几位即可. ...
- android模拟器网络设置(局域网)
Android模拟器如何设置DNS访问局域网内网站 我们需要用到android-sdk开发包中adb shell指令 见下图
- 在windows上搭建ipv6代理
事出有因,学校每天12:00之后断网,断网之后怎么办?难道直接睡了?我不甘心. 幸好学校还是留有余地,在断网之后,还是能够上ipv6的,只是现阶段互联网对ipv6支持很不理想,怎么办?刚刚发现 ...