本文简单介绍了关于Java lambda表达式及Stream操作的相关知识,包括基本概念、引入原因、简单使用方法等,供大家参考讨论。
引入版本
lambda表达式及Stream均在Java8中引入,所以开发时需要保证环境配置正确,如在IDEA上,必须保证三个地方都配置为java8以上:
- File –> Project Stucture –> Project 下的所有版本配置
- File –> Project Stucture –> Modules –> Sources –> Lanugage level
- File –> Settings –> Compiler –> Java Compiler
lambda表达式
引入原因
Java8有一个短期目标和一个长期目标。
- 短期目标是:配合“集合类批处理操作”的内部迭代和并行处理,集合类的批处理操作API的目的是实现集合类的“内部迭代”,并期望充分利用现代多核CPU进行并行计算。;
- 长期目标是:将Java向函数式编程语言这个方向引导(并不是要完全变成一门函数式编程语言,只是让它有更多的函数式编程语言的特性),也正是由于这个原因,Oracle并没有简单地使用内部类去实现lambda表达式,而是使用了一种更动态、更灵活、易于将来扩展和改变的策略。
概念
lambda表达式
“lambda表达式”(λ表达式,lambda expression):
- 是一个匿名函数(即,没有函数名的函数);
- 一个lambda 表达式其实就是定义了一个匿名方法,只不过这个方法必须符合至少一个函数接口;
- lambda 表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等。
函数接口
函数接口(Java8新引入的概念):一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。
方法引用
方法引用(Method reference):任何一个lambda表达式都可以代表某个函数接口的唯一方法的匿名描述符。我们也可以使用某个类的某个具体方法来代表这个描述符,叫做方法引用。
例如:
- Integer::parseInt //静态方法引用
- System.out::print //实例方法引用
- Person::new //构造器引用
下面是一组例子,教你使用方法引用代替λ表达式:
//c1 与 c2 是一样的(静态方法引用)
Comparator<Integer> c2 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> c1 = Integer::compare;
//下面两句是一样的(实例方法引用1)
persons.forEach(e -> System.out.println(e));
persons.forEach(System.out::println);
//下面两句是一样的(实例方法引用2)
persons.forEach(person -> person.eat());
persons.forEach(Person::eat);
//下面两句是一样的(构造器引用)
strList.stream().map(s -> new Integer(s));
strList.stream().map(Integer::new);
扩展
- Java8 Lambda表达式教程:http://blog.youkuaiyun.com/ioriogami/article/details/12782141/
Stream
引入原因
Java 8 中的 Stream 是对集合(Collection)对象功能的增强。
特性
Stream 的特性可以归纳为:
- 不是数据结构,它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
- 它也绝不修改自己所封装的底层数据结构的数据,例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
- 所有 Stream 的操作必须以 lambda 表达式为参数。
- 不支持索引访问,你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。
- 很容易生成数组或者 List。
- 惰性化,很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。Intermediate 操作永远是惰性化的。
- 并行能力,当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
- 可以是无限的,集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。
性能评估
- 对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
- 对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。
- 还有一个方法叫parallelStream(),顾名思义它和stream()一样,只不过指明要并行处理,以期充分利用现代CPU的多核特性。
适应场景
如果出于性能考虑:
- 对于简单操作推荐使用外部迭代手动实现,
- 对于复杂操作,推荐使用Stream API,
- 在多核情况下,推荐使用并行Stream API来发挥多核优势,
- 单核情况下不建议使用并行Stream API。
如果出于代码简洁性考虑:
使用Stream API能够写出更短的代码。即使是从性能方面说,尽可能的使用Stream API也具有另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。
扩展
- Java 8 中的 Streams API 详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/#icomments
- Java Stream API性能测试:http://www.importnew.com/24250.html
应用实例
典型的大数据处理方法,Filter-Map-Reduce:
//给出一个String类型的数组,找出其中所有不重复的素数
public void distinctPrimary(String... numbers) {
List<String> l = Arrays.asList(numbers);
List<Integer> r = l.stream()
.map(e -> new Integer(e))
.filter(e -> Primes.isPrime(e))
.distinct()
.collect(Collectors.toList());
System.out.println("distinctPrimary result is: " + r);
}
- 第一步:传入一系列String(假设都是合法的数字),转成一个List,然后调用stream()方法生成流。
- 第二步:调用流的map方法把每个元素由String转成Integer,得到一个新的流。map方法接受一个Function类型的参数,上面介绍了,Function是个函数接口,所以这里用λ表达式。
- 第三步:调用流的filter方法,过滤那些不是素数的数字,并得到一个新流。filter方法接受一个Predicate类型的参数,上面介绍了,Predicate是个函数接口,所以这里用λ表达式。
- 第四步:调用流的distinct方法,去掉重复,并得到一个新流。这本质上是另一个filter操作。
- 第五步:用collect方法将最终结果收集到一个List里面去。collect方法接受一个Collector类型的参数,这个参数指明如何收集最终结果。在这个例子中,结果简单地收集到一个List中。我们也可以用Collectors.toMap(e->e, e->e)把结果收集到一个Map中,它的意思是:把结果收到一个Map,用这些素数自身既作为键又作为值。toMap方法接受两个Function类型的参数,分别用以生成键和值,Function是个函数接口,所以这里都用λ表达式。
常用的收集器方法,groupingBy:
//给出一个String类型的数组,找出其中各个素数,并统计其出现次数
public void primaryOccurrence(String... numbers) {
List<String> l = Arrays.asList(numbers);
Map<Integer, Integer> r = l.stream()
.map(e -> new Integer(e))
.filter(e -> Primes.isPrime(e))
.collect( Collectors.groupingBy(p->p, Collectors.summingInt(p->1)) );
System.out.println("primaryOccurrence result is: " + r);
}
注意这一行:Collectors.groupingBy(p->p, Collectors.summingInt(p->1))它的意思是:把结果收集到一个Map中,用统计到的各个素数自身作为键,其出现次数作为值。
==================================
==疑问?帮助?批评?欢迎评论 | QQ:593159978==
==================================