Java8实战笔记( Lambda表达式学习)

什么是Lambda?

Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表,函数主体,返回类型,可能还有一个可以抛出的异常的列表。

Lambda表达式可以让你写出更简洁的代码
函数式接口就是仅仅声明了一个抽象方法的接口。
只有在接受函数式接口的地方才可以使用Lambda表达式
Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
Java8自带一些常用的函数式接口,放在java.util.function包里,包括Predicate,Function<T,R>,Supplier,Consumer和BinaryOperator
为了避免装箱操作,对Predicate和Function<T,R>等通用函数接口的原始类型特化:IntPredicate,IntToLongFunction等。
环绕执行模式(即在方法所必需的代码中间,你需要执行点儿什么操作,比如资源分配和清理)可以配合Lambda提高灵活性的可重用性。
Lambda表达式所需要代表的类型称为目标类型。
方法引用让你重复使用现有的方法实现并直接传递它们。
Comparator,Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。

比较有用的方法

比较器复合

使用Comparator.comparing根据提取用于比较的键值的Function来返回一个Comparator
Comparator c = Comparator.comparing(Apple::getWeigth);

1.逆序

inventory.sort(comparing(Apple::getWeight).reversed();//按重量排序

2.比较器链

inventory.sort(comparing(Apple::getWeight)
.reversed()                                                              //按重量递减排序
.thenComparing(Apple::getCountry));                    //两个苹果一样重时,进一步按照国家排序

谓词复合

谓词接口包括三个方法:negat、and和or
可以使用Predicate来创建更复杂的谓词

Predicate notRedApple = redApple.negate(); // 产生现有 Predicate对象redApple的非

Predicate redAndHeavyApple = redApple.and(a→a.getWeigth()>150);//链接两个谓词来//生成一个Predicate对象。

Predicate redAndHeavyAppleOrGreen = redApple.and(a→a.getWeight>150)
or(a→“green”.equals(a.getColor()));//链接Predicate的方法来构造更加复杂的Predicate对象

这种通过简单的lambda表达式出发,你可以构建更复杂的表达式,但读起来仍然和问题的陈述差不多!请注意,and和or方法是按照在表达式链中的位置,从左到右确定优先级的。
因此,a.or(b).and(c)可以看作(a||b)&&c

函数复合

可以把Function接口所代表的Lambda表达式复合起来。
Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。

AndThen方法会返回一个函数,它先对输入应用一个给定的函数,再对输出应用另一个函数。比如,假设有一个函数f给数字加1(x→x+1),另一个函数g给数字乘2,你可以将他们合成一个函数h,先给数字加1,再给结果乘2:
Function<Integer,Integer> f = x →x+1;
Function<Integer,Integer> g = x→x+2;
Function<Integer,Integer> h = f.andThen(g);//数学上会写作g(f(x))或(g o f)(x)
int result = h.apply(1); //这将返回4.

compose方法,先把给定的函数用作compose的参数里面给的那个函数,然后把函数本身用于结果。f(g(x))

Function<Integer,Integer> f = x →x+1;
Function<Integer,Integer> g = x→x+2;
Function<Integer,Integer> h = f.compose(g);//数学上会写作f(g(x))
int result = h.apply(1); //这将返回3

在实际中比方说你有一系列工具方法,你可以通过这些工具来创建各种转型流水线。

数学中的类似思想

积分

计算f(x) = x+10面积
∫f(x)dx 在[3,7]
java中
integrate(f,3,7)
而不能这么写
integrate(x+10,3,7)
第一,x的作用域不清楚,第二,这将会把x+10的值而不是函数f传给积分。
数学上dx的秘密就是说以x为自变量、结果是x+10的那个函数。
与java8的lambda联系起来
java8的表示法(double x)→x+10 (一个lambda表达式)恰恰就是为此设计的
integrate((double x)→x+10,3,7)
或者
integrate((double x)→f(x),3,7)
或者,用前面说的方法引用,只要写:
integate(c::f,3,7)
这里c是包含静态方法f的一个类。理念就是把f背后的代码传给integrate方法。
Public double integrate(DoubleFunction f,double a,double b){
return (f.apply(a)+f.apply(b))*(b-a)/2;
}
有点可惜的是,你必须写f.apply(a),而不是像数学里面写f(a);但java无法摆脱一切都是对象的思想,它不能让函数完全独立!

什么是流?

流是什么?

流是JavaAPI的新成员,它允许你以声明性方式处理数据集合(它通过查询语句来表达,而不是临时编写一个实现)。可以把它看作便利数据集的高级迭代器。此外可以透明的并行处理

Java7
List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d:menu){
    if(d.getCaloricDishes()<400){
        lowCaloricDishes.add(d);
    }
}
Collections.sort(lowCaloricDishes,new Comparator<Dish>(){
    public int compare(Dish d1,Dish d2){
        return Integer.compare(d1.getCalories(),d2.getCalories());
    }
});
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d:lowCaloricDishes){
    lowCaloricDishesName.add(d.getName());
}
Java8
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName = menu.stream()
    .filter(d->d.getCalories()<400)
    .sorted(comparing(Dish::getCalories))
    .map(Dish::getName)
    .collect(toList());  

利用多核

List<String> lowCaloricDishesName = 
menu.parallelStream()
    .filter(d->d.getCalories()<400)
    .sorted(comparing(Dish::getCalories))
    .map(Dish::getName)
    .collect(toList());  
这样写的好处

代码是以声明性方式写的:说明想要完成什么而不是说明如何实现一个操作(利用循环和if等控制流语句),利用行为参数化让你很容易创建一个代码版本,利用Lambda表达式来筛选高卡路里的菜肴,而不用筛选复制粘贴代码。
可以把你个基础操作链接起来,来表达复杂的数据处理流水线,同时保持代码清晰可读。
因为filter、sorted、map和collect等操作是与具体线程模型无关的高层次构件,所有它们的内部实现可以单线程的,也可能透明地充分利用你的多核构架。这意味着你用不着为了让某些数据处理任务并行而去操心线程和锁了,Stream API都替你做好了。
Map<Dish.Type,List> dishesByType = menu.stream().collect(groupingBy(Dish::getType));//按照Map里面的类别对菜肴进行分组
StreamAPI可以让你写出
声明性——更简洁,更易读
可复合——更灵活
可并行——性能更好

流简介

Java8集合支持一个新的stream方法。返回一个流。

流:从支持数据处理操作的源生成的元素序列。
元素序列——就像集合一样,流也提供了一个借口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(ArrayList和LinkedList)。但流的目的在于表达计算。比如你前面见到的filter、sorted和map.集合讲的是数据,流讲的是计算。
源:流会使用一个提供数据的源,如集合,数组或输入输出资源。从有序集合生成流时会保留原有的顺序。
数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作。如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。

流操作的两个重要特点:
流水线:很多流操作本身会返回一个流,这个多个操作可以链接起来,形成一个大的流水线。流水线的操作可以看作对数据源进行数据库式查询。
内部迭代:与使用迭代器显式迭代集合不同,流的迭代操作是在背后进行的。

import static java.util.stream.Collection.toList;
List<String> threeHighCaloricDishNames = menu.stream()
.filter(d->d.getCalories()>300)
.map(Dish::getName)//获取菜名
.limit(3)          //只选择头三个
.collect(toList());//将结果保存在另一个List中
System.out.println(threeHighCaloricDishNames);

filter——接受lambda,从流中排除某些元素。
map ——接受lambda,将元素转换成其他形式或提取信息。
limit ——截断流,使其元素不超过给定数量。

集合与流

Java现有的集合概念和新的流概念都提供了接口,来配合代表元素型有序值得数据接口。
所谓有序就是按照一般的顺序取用值,而不是随机取用的。
集合和流的差异就是在于什么时候进行计算。集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素就得先算出来才能添加到集合中。相比之下流则是概念上固定的数据结构,其元素是按需计算的。这是一种生产者消费者的关系。从另一个角度来说,流就是像一个延迟创建的集合:只有在消费者要求的时候才会计算值。(按需驱动,实时制造)
两个例子:
1.用浏览器进行互联网搜索
2.用DVD对比在线流媒体的例子展示了流和集合之间的差异
流只能遍历一次。

内部迭代与外部迭代

使用Collection接口需要用户去做迭代(比如for-each),这称为外部迭代
Stream库使用内部迭代——它帮你把迭代做了,还得得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。
流:内部迭代
List names = menu.stream().map(Dish::getName).collect(toList());//使用getName方法参数化map,提取菜名。
Stream库的内部迭代可以自动选择一种适合你硬件的数据表示和并行实现。与此相反,一旦通过for-each而选择了外部迭代,那你就要自己管理所有的并行问题了。

流操作

Stream接口定义了许多操作。它们可以分为两大类。
1.filter、map和limit可以连成一条流水线
2.collect触发流水线执行并关闭它
可以连接起来的流操作成为中间操作
关闭流的操作称为终端操作。

中间操作与终端操作

limit操作和一种称为短路的技巧。
filter和map是两个独立的操作,但它们合并到同一次遍历中了。这种技术叫做循环合并。
终端操作会从流的流水线生成结果。其结果是任何不是任何流的值。

使用流:

一个数据源来执行一个查询
一个中间操作莲,形成一条流的流水线
一个终端操作,执行流水线,并能生成结果。

中间
filter map limit sorted distinct

终端
forEach count collect

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NewTech精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值