定义
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
java8增加了对函数式编程的支撑,即java8 lambda表达式。通过lambda表达式可以方便程序员将代码作为数据进行传递,可以赋值给一个变量也可以作为函数参数、返回值来使用,类似于java的匿名内部类。
入门例子
当前开发使用最多的就是多线程,
- 在java中一般是通过实现Runnable接口,如下
new Thread(new Runnable() { @Override public void run() { System.out.println("Hello world!"); } }).start();
- 在java8中可以使用lambda表达式形式,更简单,如下
new Thread( () -> System.out.println("Hello world!") ).start();
java8 lambda语法
- 基本语法
(parameters) -> expression
或
(parameters) ->{ statements; }
说明:parameters可以带参数类型也可以省略参数类型,但省略参数类型时必须能保证jdk自动推断
- 函数接口
函数接口是只有一个抽象方法的接口,用作Lambda表达式的类型,可以使用@FunctionalInterface注解标注。接口中单一方法的命令并不重要,只要方法签名和Lambda表达式的类型匹配。
- JDK中默认提供的函数接口
接口 | 参数 | 返回类型 | 示例 |
---|---|---|---|
Predicate<T> | T | boolean | 判断结果的真假,是与否等 |
Consumer<T> | T | void | 输出一个值,像上面线程 |
Function<T,R> | T | R | 获得一个对象的名字 |
Supplier<T> | None | T | 工厂方法 |
UnaryOperator<T> | T | T | 逻辑非(!) |
BinaryOperator<T> | (T,T) | T | 求两个数的乘积(*) |
java8 Stream
java8 不仅在语言层面支持了lambda表达式,还为此对类库做了改进,包括集合类的API以及引入的流(Stream),Stream很多方法的参数都是lambda表达式。
-
内部迭代和外部迭代
相对于集合代码来说的,像常用的for/while循环通过产生新的Iterator对象来控制整个迭代过程,就是外部迭代。而java8中集合对你的stream()方法直接是使用Stream对集合中的数据进行迭代,就是内部代码。List<String> list = new ArrayList<>(); list.add("hello"); list.add("world"); list.add("!"); //外部迭代 for (String s : list) { System.out.println(s); } //内部迭代 list.stream().forEach(e -> System.out.println(e));
-
惰性求值
在Stream的操作中,有一些像filter方法只描述Stream,并不会产生一个新的集合的方法,叫做惰性求值方法;就像医生开的药方,并不是具体的药品。 -
及早求值
在Stream的操作中,还有一类像count这种方法最终会从Stream中产生最终值,叫做及早求值方法;这个已经是根据药方获取到相应药品了。
判断惰性求值还是及早求值很简单:返回Stream对象就是惰性求值;返回另外类型的值或者空,则是及早求值。
对Stream最理想的操作是形成一个惰性求值链,最后用一个及早求值的操作返回想要的结果。
long num = list.stream().filter(s -> s.length() > 2).count();
System.out.println("num: " + num);
----------------------------------------------------------
输出:num: 2
java8 常用的Stream操作
- collect(toList())
将一个流中的值生成一个列表,及早求值函数。是一个通用的结构。
List<String> list = Stream.of("hello", "world", "!").collect(Collectors.toList());
- map
如果一个函数可以将一种类型的值转换成另外一种类型,则map就可以使用此函数将一个流中的值转换成一个新的流。例如将字符串字符全大写
List<String> collect = Stream.of("hello", "world", "!").map(s -> s.toUpperCase()).collect(Collectors.toList());
collect.stream().forEach(s -> System.out.print(s + " "));
------------------------------------------------------
输出:HELLO WORLD !
map函数接收一个Function<T,R>类型的参数。
- filter
遍历数据并检查其中的元素是filer的本职工作。例子可以参考前面的。
filter函数增收一个Predicate<T>类型的参数
- flatMap
与map类似,可以将Stream中的值进行替换;与map不一样的是flatMap可以将多个Stream连接成一个Stream
List<String> together = Stream.of(asList("a", "b"), asList("c", "d")).flatMap(s -> s.stream()).collect(Collectors.toList());
与map一样接收一个Function<T,R>类型参数,不过R的类型限定为一个Stream类型。
- max和min
Stream上常用的操作之一是求最大或者最小值。这个要考虑的是用什么作为排序的指标,例如数值大小、字符串长度、字典序等等。
System.out.println("max: " + Stream.of(asList(1, 2), asList(3, 4)).flatMap(s -> s.stream()).max(Comparator.comparing(n -> n)).get());
System.out.println("min: " + Stream.of(asList(1, 2), asList(3, 4)).flatMap(s -> s.stream()).min(Comparator.comparing(n -> n)).get());
----------------------------------------------------------------------
输出:max: 4
min: 1
接收一个Comparator对象,此类提供了一个静态函数comparing可以方便用户实现一个比较器。此函数参数、返回值都是lambda表达式。返回的是一个Optional对象(后面再专门讨论),需要使用get方法获取相应的值。
- reduce
实现从一组值中生成一个值。前面例子中的count、max、min方法就是reduce操作,只是已经纳入了标准库中了。使用reduce实现数字累加,如下
System.out.println("sum: " + Stream.of(1,2,3).reduce(0, (acc,elment) -> acc + elment));
------------------------------------
输出:sum: 6
reduce第1个参数是初始值,第二个参数叫reducer,是一个BinaryOperator类型的lambda表达式。
参考
《java 8函数式编程》
http://www.importnew.com/16436.html
网友评论