丰乐

《java8实战阅读笔记》

作为一个对java8不是特别理解的人,花了两个星期全面的读了下《java8实战》做了个流水的笔记。

现在线上的java都是基于java8的,以后尽量使用java8的特性编程。

一下的内容都是基于markdown的。。。 

 

# java8 实战

## 2 通过行为参数传递代码


行为的参数化

1 值参数化
2 类,匿名类,lambda


## 3 lambda表达式

lambda表达式

Comparator<Apple> byWeight =(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

函数式接口

仅仅定义了一个抽象方法

函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作 函数描述符。

(Apple, Apple) -> int

只有在需要函数式接口的时候才可以传递Lambda

环绕式执行

try{}{
}

java的函数式接口之predicate

test(T t)

函数式接口Consumer

accept(T t)

函数式接口Function

R apply(T t)

抽象方法Supplier

T get()

装箱和非装箱

IntPredicate
IntConsume
int

函数类型检查:符合函数特征即可用

函数变量不推荐使用局部变量,使用时加上final


闭包

闭包就是一个函数的实例,且它可以无限 制地访问那个函数的非本地变量


方法引用

闭包就是一个函数的实例,且它可以无限 制地访问那个函数的非本地变量

三类方法引用

1 静态方法
A::XX
2 指向任意类型实例方法的方法引用
B:xx
3 现有对象的实例方法的方法引用
c:xx

构造函数引用

A::new

List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);


数组构造函数和父类调用

自动生成比较器

Comparator.comparing((Apple a) -> a.getWeight());

inventory.sort(comparing(Apple::getWeight).reversed());

inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));

Predicate谓词复合

Predicate<Apple> redAndHeavyAppleOrGreen =
readApple.and(a->a.getWeight() > b.getWeight())
.or(a -> "green".equals(a.getColor()));

function函数复合

andThen,先执行,再执行另外一个函数
先把给定的函数用作compose的参数里面给的那个函 数,然后再把函数本身用于结果。


## 4 流

流的定义

1 java的api
2 声明处理数据集合
3 高级的数据迭代器

流的基本定义

1 元素序列 filter sorted map
2 源。数据源:
3 数据处理操作
filter map reduce find match sort
limit
4 流水线
5 内部迭代

流与集合

1 流只能遍历一次
2 集合的外部迭代,流的内部迭代

流的操作

1 中间操作

filter map limit sorted distinct

2 终止操作

collect
foreach
count

## 5 使用流


谓词筛选

filter
distinct
limit
skip

映射

map
flatmap

查找和匹配

allMatch anyMatch noneMatch
findFirst findAny

optional操作

isPresent
ifPresent
T get()
T orElse(T other)

规约

reduce(init, func)
reduce(min/max)
count

所有的操作

filter
distinct
skip
limit
map
flatMap
sorted
anyMatch
noneMatch
allMatch
findAny
findFirst
forEach
collect
reduce
count


数值流

mapToInt
boxed
IntStream.rangeClosed

构建流

stream.of();
stream.empty();
Arrays.stream(numes);
File.lines(
xxx).distinct().count

函数创建流

stream.iterate
Stream.generate


## 6 使用流收集数据


### 6.1 收集器 & 规约 汇总

总数

Collectors.counting()

最大的 Collectors.maxBy(Comparator.comparingInt(Transaction::getValue))

Collectors.sumInt()
averagingInt
joining
reducie
redcing()
menu.stream().mapToInt(Dish::getCalories).sum();


### 6.2分组

groupingBy 分组,多级分组

按子组收集数据

将收集到的结果转换为另外一种类型collectAndThen

### 6.3 分区

boolean分词作为分区:partitioningBy

collection的静态方法

toList
toSet
toCollection
counting
summingint
averagingInt
joning
maxBy
minBy
reducing
collectionAndThen
groupingBy
partitioningBy

收集器接口

自己实现java的stream

```
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
}
```

 

## 7 并行数据处理与性能

并行流

parallel

并行流性能影响

1 减少数字的装箱和开箱
2 共享可变状态会影响并行流以及并行计算。
3 如果有疑问需要测量
4 有些操作并行比线性差
5 流水线总成本
6 较小数据量时不适应
7 背后的数据结构是否易于分解
8 中间合并步骤的代价

流的源,可分解性

ArrayList 极佳
LinkedList 差
IntStream.range 极佳
Stream.iterate 差
HashSet 好
TreeSet 好


分支/合并框架

它实现ExecutorService接口,把任务分配给ForkJoinPool

任务实现返回结果:RecursiveTask<R>
不返回结果 RecursiveAction

forkjoin的业务逻辑

```
if (任务足够小或不可分) {
顺序计算该任务
} else {
将任务分成两个子任务
递归调用本方法,拆分每个子任务,等待所有子任务完成
合并每个子任务的结果
}
```

使用分支/合并框架的最佳做法

1 对一个任务调用join方法会阻塞调用方,直到该任务做出结果。两任务均开始后再调用
2 不应该在RecursiveTask内部使用ForkJoinPool的invoke方法。你应该始终直接调用compute或fork方法,只有顺序代码才应该用invoke来启动并行计算。
3 对子任务调用fork方法可以把它排进ForkJoinPool。
4 调试使用分支/合并框架的并行计算可能有点棘手。
5 和并行流一样,你不应理所当然地认为在多核处理器上使用分支/合并框架就比顺序计算快。

原理工作窃取


spliterator 可分迭代器

```
public interface Spliterator<T> {
boolean tryAdvance(Consumer<? super T> action);
Spliterator<T> trySplit();
long estimateSize();
int characteristics();
}
```

 

 

## 8 重构,测试和调试


改善可读写和灵活性

1 使用匿名类代替lambda
2 stream的方法引用
3 切换到stream

增加代码的灵活性

1 使用函数式接口
2 延迟执行
3 环绕执行

使用lambda重构设计模式

1 策略模式,函数式接口
2


lambda表达式使用

peek日志调试流


## 9 默认方法

接口可以使用默认反复


默认接口方法冲突的问题

1 类最优
2 子接口最优
3 显示覆盖



## 10 Optional取代null

创建


提取

map 获取Optional里面的数据
flatMap 从其他部分获取Optional
get
orElse
orElseGet
orElseThroww
ifPresent
filer过滤

详细操作

empty
filter
flatMAP
get
ifPresent
isPresent
map
of ofNullable orElse orElseGet orElseThrow

OptionalUtility工具


## 11 CompletableFuture

future工具

```
ExecutorService executor = Executors.newCachedThreadPool(); Future<Double> future = executor.submit(new Callable<Double>() {
public Double call() {
})

try {
Double result = future.get(1, TimeUnit.SECONDS);
} catch (ExecutionException ee) { // 计算抛出一个异常
} catch (InterruptedException ie) { // 当前线程在等待过程中被中断
} catch (TimeoutException te) { // 在Future对象完成之前超过已过期
}

```
future的局限性

1 Future很难直接表述多个Future 结果之间的依赖性,开发中,我们经常需要达成以下目的:
2 将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
3 等待 Future 集合中的所有任务都完成。
4 仅等待 Future 集合中最快结束的任务完成,并返回它的结果。


CompletableFuture

只有当每个操作很复杂需要花费相对很长的时间

基本用法

```
Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会在ForkJoinPool.commonPool()中执行。下面很多方法都是类似的,不再做特别说明。

四个静态方法用来为一段异步执行的代码创建CompletableFuture对象,方法的参数类型都是函数式接口,所以可以使用lambda表达式实现异步任务

runAsync方法:它以Runnabel函数式接口类型为参数,所以CompletableFuture的计算结果为空。

supplyAsync方法以Supplier<U>函数式接口类型为参数,CompletableFuture的计算结果类型为U。
```


```
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);

```

 

## 12 新的日期和时间API

新的日期库出现的原因


新日期类

LocationDate LocationTime instant Duration period

时间范围 Period Duration


操作和解析

 

不同时区和历法

 


## 13 函数式的思考

实现和维护系统

1 类的结构合理
2 提供指标
3 耦合性,内聚性

难题

1 共享的可变数据

命令式编程


声明式编程

称述问题

函数式编程:用函数进行编程的方式

1 修改本地变量
2 只引用不可修改的对象
3 函数或者方法不能抛出异常 使用Option<T>

引用的透明性:同一值,输出不变

面向对象和函数式的对比

1 万物是对象
2 引用透明,不应该有修改对象

考虑编程问题时,采用函数式的方法,关注函数的输入参数以及输出结果


递归和迭代

函数编程不会包含while和for的迭代器

## 14 函数式编程的技巧


### 无处不在的函数

高阶函数:至少一个函数为参数,返回一个函数,注意函数式的副作用

科里化:返回函数


### 持久化数据结构

函数式方法不能修改传入参数或者全局参数

### stream的延迟计算?

stream只允许使用一次

stream不允许递归定义

延迟列表


### 模式匹配


### 杂项

缓存或记忆表

stream的特定

1.stream不存储数据
2.stream不改变源数据
3.stream的延迟执行特性


## 15 面向对象和函数式编程的混合

jvm平台函数式语言 scala

 

scala 更好的流式编程,更好的函数式编程


## 16 结论以及java的未来

尽量使用新特性

 

 

 

 

 

 

 

 

 

 

 

 







 

posted on 2019-08-11 10:11  李蝉儿  阅读(303)  评论(0编辑  收藏  举报

导航