lambda stream Future

java8实战 随笔摘抄备忘

 

lambda基本上是一个没有声明名称的方法

可以十分简明的传递代码

lambda隐含return语句

       对于    ()-> 42   这段代码   无入参,但会返回int类型值42

 

在函数式接口中使用 lambda

lambda表达式为函数式接口的抽象方法提供实现.

lambda表达式可以赋值给一个变量  Runnable r1 = () -> System.out.println("Hello World 1");    -----r1是个变量

 

lambda表达式可以为函数式接口生成一个实例,然而,lambda表达式本身并不包含它在实现哪个函数式接口的信息.

同样的lambda,不同的函数式接口

自动推断类型,所以入参可以写成 e

 

lambda使用局部变量

要求:局部变量显示声明为final,或事实上最终的

因为:实例变量存储在堆中,局部变量保存在栈上.(方法保存在栈里,局部变量在方法中)

目的:防止线程不安全

 

 

方法引用

方法引用就是让你根据已有的方法实现来创建lambda表达式,方法引用就是lambda表达式的快捷写法

 

使用流

数值流,文件流,数组流,无限流

 

filter:结果是两类 一类满足筛选条件,一类不满足筛选条件

筛选各异的元素,就是去重,用distinct方法:

截短流,limit

跳过元素 skip(n)方法.扔掉前n个元素

 

映射

mapflatmap方法,类似于SQL中选择某一列

       map方法会接受一个函数作为参数

 

map 不光是对对象流进行单字段的“截取”,还可以做“映射”,传一个函数进map(Function f).

流的扁平化 flatmap

flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流

例题: (2) 给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4],应
该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见,你可以用有两个元素的数组来代
表数对。

答案:你可以使用两个map来迭代这两个列表,并生成数对。但这样会返回一个Stream-
<Stream<Integer[]>>
。你需要让生成的流扁平化,以得到一个Stream<Integer[]>。这
正是flatMap所做的:
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs =
numbers1.stream()

.flatMap(i -> numbers2.stream()
.map(j -> new int[]{i, j})
)

.collect(toList());

 

(3) 如何扩展前一个例子,只返回总和能被3整除的数对呢?例如(2, 4)(3, 3)是可以的。
答案:你在前面看到了, filter可以配合谓词使用来筛选流中的元素。因为在flatMap
操作后,你有了一个代表数对的int[]流,所以你只需要一个谓词来检查总和是否能被3整除
就可以了:
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs =
numbers1.stream()
.flatMap(i ->
numbers2.stream()

.filter(j -> (i + j) % 3 == 0)
.map(j -> new int[]{i, j})
)
.collect(toList());

其结果是[(2, 4), (3, 3)]

 

 

切片和筛选

filter distinct(可以使用Collect.toSet()代替) limit  skip

查找和匹配

find match

是否至少匹配一个      anyMatch()    返回boolean

是否匹配所有              allMatch()     

是否无匹配                 noneMatch()

 

findAny()       只返回一个值,可以与其他流操作结合使用

注意: 在串行的流中,findAny和findFirst返回的,都是第一个对象;而在并行的流中,findAny返回的是最快处理完的那个线程的数据,所以说,在并行操作中,对数据没有顺序上的要求,那么findAny的效率会比findFirst要快的

findAny()之后这个流就会关闭,ifPresent()是Optional类的方法.

 

findFirst()       有序流查找第一个元素

 

归约(将流归约成一个值)

将流中的元素组合起来,使用reduce操作来表达更复杂的查询

元素求和

reduce接受两个参数:

一个初始值,

一个BinaryOperator<T>  可以是lambda表达式,可以是Function 接口.对于求和可以用Integer::sum

reduce还有一个重载的变体,它不接受初始值,所以返回可能为空,因此返回一个Optional对象.

 

最大值和最小值

Optional<Integer> max = numbers.stream().reduce(Integer::max);

Optional<Integer> min = numbers.stream().reduce(Integer::min);

流支持min和max方法:

       stream.min(comparing(Function f));

 

 

map-reduce模式,因Google用它来进行网络搜索而出名,因为它很容易进行并行化。

 

统计个数

long count = menu.stream().count();

 

使用场景模拟:

如果涉及对字符串的连接,使用collect(joining())

 

数值流

原始类型流特化,避免封装类型的性能损耗

 

映射到数值流:

              stream().mapToInt(Function f).sum();

                                                                .max()

                                                                .min()

                                                                .average()

转换为对象流:

       stream.mapToInt().boxed()

默认值OptionalInt

 

数值范围

       IntStream.range(start,end)和rangeClosed(start,end).前一个不包含结束值,后一个包含结束值end

       作用是,生成从start到end的数值流

 

从值序列、数组、文件来创建流,甚至由生成函数来创建无限流

由值创建流          Stream.of()

由数组创建流       Arrays.stream()

由文件生成流       Files.lines()

由函数生成流

       迭代       Stream.iterate(初始值,lambda表达式).limit();

       应用:如何取数组流中每个数组中的第一个元素   stream.map(e->e[0])

       生成       Stream.generate(Math::random).limit()

 

 

 

第六章 用流收集数据

 

collect是一个归约操作,就像reduce一样可以接

受各种做法作为参数,将流中的元素累积成一个汇总结果。具体的做法是通过定义新的

Collector接口来定义的,因此区分Collection、 Collector和collect是很重要的。

 

分组

多级分组

把不同的收集齐结合起来,对每个子组进行进一步归约

 

预定义收集器提供了三大功能:

流元素归约,元素分组,元素分区

 

归约和汇总

stream().collect(Collectors.counting());

这样写不如stream().count(),有多个收集器的时候才能起作用

 

menu.stream().collect(summingInt(Dish::getCalories))         求和

menu.stream().collect(averagingInt(Dish::getCalories));       求平均

map()操作之后也可以求和求平均,还能求最值

 

如果要一次性获取多个统计数据,可以用summarizingInt(Dish::getCalories)

 

连接字符串

joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。

这意味着你把菜单中所有菜肴的名称连接起来,如下所示:
String shortMenu = menu.stream().map(Dish::getName).collect(joining());

 

但该字符串的可读性并不好。幸好, joining工厂方法有一个重载版本可以接受元素之间的分界符,

这样你就可以得到一个逗号分隔的菜肴名称列表:
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

 

collect方法和reduce方法的对比,collect方法更适合并行操作

collect在归约的时候要传3个参数,初始值,转换函数,累积函数。reduce()其实就是它的简化版

 

结论:最值用mapToInt()

 

 

分组

按类型值去分组:groupingBy(类型值)

按范围去分组:自定义分组逻辑

public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} ));

 

多级分组

使用一个由双参数版本的Collectors.groupingBy工厂方法创建的收集器

第一个参数还是普通的分类函数,第二个参数是Collector(比如groupingBy(),里面还可以继续套groupingBy())。生成一个双层map

二级分组相当于一个二维表

 

第二个参数Collector可以是groupingBy(),但不限于此。第二个收集器可以是任何类型,

比如可以是counting()。这样就可以实现分类并统计个数

也可以是maxBy(comparingInt())。这样就可以实现分类并取最大值

 

把收集器返回的结果转化为另一种类型

Collectors.collectingAndThen()工厂方法

接受两个参数—要转换的收集器和转换函数。

 

 

分区

分区是特殊的分组,由一个返回布尔值的函数作为分类函数。最多只能分两组,true一组,false一组

partitioningBy()

partitioningBy(分区函数,groupingBy())       结果先分两类,再做细分

 

 

收集器接口

实现Collector接口从而自由创建自定义归约操作。

 

有点麻烦

 

 

第七章  并行数据处理与性能

 

在调用那个看似神奇的parallel操作时,了解背后到底发生了什么是很有必要的

多测试看看是否真的并行处理了.当操作数字流的时候一定要注意避免装箱/拆箱上的性能损耗.

使用正确的数据结构然后使其并行工作能够保证最佳的性能

避免使用共享变量

如果是较少的数据量,不建议并行

要考虑流背后的数据机构是否易于分解

要考虑合并步骤的代价是大是小

 

第八章 重构,测试和调试

 

  • 使用lambda重构面向对象的设计模式

策略模式

一个算法接口,             实质是一个函数接口,定义各个函数

多个算法实现,            

多个策略对象客户

 

对于上面的函数接口,可以用lambda表达式代替实体类

 

模板方法

在搭建一个框架的同时又需要一定的灵活度,能对某部分进行修改  就像函数

可修改部分就可以用lambda表达式实现

 

好像要麻烦一点,需要构造功能函数?要用到.accept()方法

 

观察者模式

需要一个观察者接口,仅有一个notify的方法.不同的观察者实现不同的方法

不太适合全盘使用lambda表达式

 

责任链模式

是一种创建处理对象序列的通用方案,方法A处理完了将任务传给方法B去处理,依次类推

改用lambda表达式,可以用A.andThan(B)方法来处理.

 

工厂模式

提供一个方法,根据传入的参数不同而创建并返回不同的对象

拓展性不强

 

  • 测试

如何测试lambda表达式,方法栈不太方便,因为是匿名函数,所以不太好定位

可以使用日志调试

在方法链之间插入peek(lambda表达式)方法.

 

第十一章 CompletableFuture:组合式异步编程

 

并发:单核处理多任务

并行:多核处理任务

 

  • CompletableFuture是Future的新版实现

 

这种编程方式让你的线程可以在ExecutorService以并发方式调
用另一个线程执行耗时操作的同时,去执行一些其他的任务。接着,如果你已经运行到没有异步
操作的结果就无法继续任何有意义的工作时,可以调用它的get方法去获取操作的结果。如果操
作已经完成,该方法会立刻返回操作的结果,否则它会阻塞你的线程,直到操作完成,返回相应
的结果。

 

  • 使用CompletableFuture构建异步应用

同步API:甲调用乙,在乙运行时甲会等待,即阻塞式调用。

异步API:甲调用乙,但是甲跟乙不在同一个线程,甲和乙是异步的,即非阻塞式调用。乙会将计算结果返回给甲。

 

实现异步API

Future是一个暂时还不可知值的处理器,调用其get()方法时会发生阻塞.

 

  • 将同步方法转换为异步方法

错误处理,获取任务线程的异常信息

public Future<Double> getPriceAsync(String product) {
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread( () -> {
try {
double price = calculatePrice(product);

futurePrice.complete(price);
} catch (Exception ex) {

futurePrice.completeExceptionally(ex);
}
}).start();
return futurePrice;
}

 

使用CompletableFuture发起异步请求

并行流默认使用的线程池大小就是机器核心数

使用定制的执行器可以自定义线程池大小

 

设I/O的时间是a,接口总用时是b 等待时间就是a,计算时间是(b-a)

比率是:a/(b-a)

 

计算密集型:处理数据量大,过程计算步骤多,使用机器的默认线程即可.

涉及I/O操作:每次计算量比较小,不能充分利用单核的计算能力,适合多创建线程.

 

 

对多个异步任务进行流水线操作

上面所说的每个Future中进行的都是单次操作,接下来会将多个异步操作结合在一起,以流水线的方式运行

 

使用CompletableFutures可能比使用并行流要慢一些,如果不考虑线程个数的话

因为并行流使用默认的线程数

 

  • 使用定制的执行器

private final Executor executor =
Executors.newFixedThreadPool(Math.min(shops.size(), 100),
new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
});
 

1.创建一个线
程池,线程
池中线程的
数 目为 100
和商店数目
二者中较小
的一个值
2.使用守护线程——这
种方式不会阻止程序
的关停

 

                            异步à同步à异步

如果执行两次异步操作,用了两个不同的CompletableFuture对象,还希望将他们以级联的方式串联起来进行工作.

那么需要使用thenCompose方法.它允许你对两个异步操作进行流水线

 

换句话说,你可以创建两个CompletableFuture对象,对第一个CompletableFuture对象调用thencompose方法,并向其传递一个函数.

 

thencompose方法还有一个以Async结尾的异步方法

 

将两个CompletableFuture对象整合起来,无论他们是否存在依赖

一种常见的情况是:你需要将两个完全不相关的completablefuture对象的结果整合起来。而且二者之间没有依赖性

也就是两个completablefuture之间是并联关系,用thenCombine()方法

 

这种情况,你应该使用thenCombine方法,它接收名为BiFunction的第二参数,这个参数
定义了当两个CompletableFuture对象完成计算后,结果如何合并。

如何处理结果作为第二个参数

 

 

响应completablefuture的completion事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值