您的位置:首页 > 编程语言 > Java开发

初探Lambda表达式-Java多核编程【0】从外部迭代到内部迭代

2017-02-09 17:03 441 查看

开篇

放假前从学校图书馆中借来一本书,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循环遍历本身,只不过对并发做了一些处理而已。

可见外部迭代与内部迭代并没有本质上的区别,两者存在形式上的不同。

内部迭代的更多优势与特性随着本书的深入将会逐渐显现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐