初探Lambda表达式-Java多核编程【0】从外部迭代到内部迭代
2017-02-11 15:15
561 查看
开篇
放假前从学校图书馆中借来一本书,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!"));
->前的s为参数名(即
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 Ni 4000 co --- 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!
此外我们再进入forEach方法一看究竟:
@Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
可以发现其内部依旧是使用了一个for循环遍历本身,只不过对并发做了一些处理而已。
可见外部迭代与内部迭代并没有本质上的区别,两者存在形式上的不同。
内部迭代的更多优势与特性随着本书的深入将会逐渐显现。
相关文章推荐
- 初探Lambda表达式-Java多核编程【0】从外部迭代到内部迭代
- 初探Lambda表达式-Java多核编程【0】从外部迭代到内部迭代
- 初探Lambda表达式-Java多核编程【0】从外部迭代到内部迭代
- java8学习之内部迭代与外部迭代本质剖析及流本源分析
- javah javac 不是内部或外部命令 解决方法
- java、java -version能正常运行、但javac不是内部或外部命令,有可能是变量位置问题!
- Windows7中Java64位环境变量配置:javac不是内部命令或外部命令,也不是可运行的程序或批处理文件。
- 'D:\android\ANDROI~1\tools\lib\\find_java.exe' 不是内部或外部命令,也不是可运行的程序或批处理文件。
- 运行javac 报告javac不是内部或外部命令,但是运行java、java-version正常
- JAVA安装及‘Javac’ 不是内部或外部命令
- win10配置java环境变量,解决javac不是内部或外部命令等问题
- 运行javac 报告javac不是内部或外部命令,但是运行java、java-version正常
- java中外部接口与内部接口的使用
- <s:iterator 迭代双层MAP,内部迭代取外部KEY
- java环境变量path也配置了,但还是显示javac不是内部或外部命令的解决办法
- Tomcat学习笔记 - 错误日志 - Tomcat安装版安装后第二次启动后闪退(转)-- javac不是内部或外部命令 -- 配置java环境教程
- 解决打开Android SDK Manager时出现“.....\sdk\tools\lib\\find_java.exe不是内部或外部命令,也不是可运行的程序或批处理文件“问题
- 解决:java不是内部或外部命令 也不是可运行的程序
- java 不是内部或外部命令,也不是可运行程序
- java中内部方法对外部类变量的引用