有了前面的 SAM,Lambda 表达式,以及默认接口方法作铺垫后,我们可以去很好的去理解 Java8 用 Lambda 表达式操作集合的基本原理了。此篇我们想要化解的示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package cc.unmi; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * @author Unmi */ public class TestJava8Collection { public static void main(String[] args) { Collection collection = Arrays.asList("abc", "cde", "efg"); List list = collection.stream().filter(x -> x.contains("c")).collect(Collectors.toList()); list.forEach(x->System.out.println(x)); } } |
如果对其他支持闭包的语言,如 JavaScript, Groovy, Ruby, Scala 等有所了解的话,很容易看出前面的代码输出为
abc
cde
因为这两个元素含有字母 "c"。
前面演示的是对集合过滤后,再次构成一个新的集合,最后用 forEach 输出所有元素。如果无需返回新的集合,只是过滤输出的话,只要一句话就行
collection.stream().filter(x -> x.contains("c")).forEach(x->System.out.println(x));
用不着回味相比于 JDK 本身,原来用 Apache Commons Collections 似乎为我们处理集合带来了不少便利,在 Lambda 面前真的不值一提。
在用 Lambda 进行集合操作基本都是先匹配到 Stream 类型上去,因为我们只知 collection 是一个接口类型,只有 Collection 的默认接口方法靠得住,它有哪几个默认方法呢,四:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public interface Collection extends Iterable { ................. default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; } @Override default Spliterator spliterator() { return Spliterators.spliterator(this, 0); } default Stream stream() { return StreamSupport.stream(spliterator(), false); } default Stream parallelStream() { return StreamSupport.stream(spliterator(), true); } } |
Collection 的父接口是 Iterable 中也有默认方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public interface Iterable { default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } default Spliterator spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } } |
所以对于 Collection 类型也可以直接调用 forEach() 方法。
这里默认接口方法就是担当的一个桥梁的作用。待得到了 Stream 实例后就可以好好去享用如此之多的集合处理函数
为什么说有了 Stream 就可以用 Lambda 来处理了呢,因为它的众多方法参数是功能性接口,所以能够用 Lambda 表达式与 SAM 匹配上构造出功能性接口的实例。如 Function, Consumer, Predicate, Collector, Comparator, BinaryOperator 等比比皆是功能性接口。
所以仍然那句话,Java8 中想要 Lambda 友好性,就必须往功能性接口上靠,寻找你的 SAM。
写到此,本以为会有不少内容值得发挥,没想,初见 collection.stream() 时的那种神秘感已悄然褪去,stream() 不过是一个接口的默认方法,真正好戏在它后头,其后亦不过功能性接口(SAM),仅此而已。
本文链接 https://yanbin.blog/jdk8-lambda-collection-operations/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。