Java 8 实战
前言
Why Java 8?
代码简洁,可读性高,不改变原值(不可见、范型)
// 输入 abdeeeea;输出 eeeeaabd
private static void jdk7sort(List<Map.Entry<Character, Integer>> list) {
Collections.sort(list, new Comparator<Map.Entry<Character, Integer>>() {
@Override
public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
{
int firstCount = o1.getValue();
int secondCount = o2.getValue();
if (firstCount != secondCount) {
return secondCount - firstCount;
}
return o1.getKey().charValue() - o2.getKey().charValue();
}
}
});
}
private static void jdk8sortAndThenCompare(List<Map.Entry<Character, Integer>> list) {
list.sort(Map.Entry.<Character, Integer>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()));
}
-
Cool(语法糖)不改语法结构,运行时等价,语义自然
Lambda 表达式
Lambda-函数式编程
- 拼接行为(搭积木 复合)像linux命令: cat tmp.md |grep GET ,其中的|、 cat 、 grep为操作,tmp.md、GET、返回结果为输出。
- 降低匿名类的创建成本
- 环绕执行模式
- 行为参数化
- 使用函数式接口来传递行为
- 执行一个行为
- 传递lambda
Lambada-接口
Lambada 打开方式
- 函数式接口定义输入、输出,用户定义法则,共同构成函数-公式
煮个🌰 勾股定理
1.
两个入参一个返回参数,选
BiFinction
,
2.
定义法则
(
x, y
)
->
( int ) ( Math. sqrt ( Math. pow ( x, 2 ) + Math. pow ( y, 2 ))
( int ) ( Math. sqrt ( Math. pow ( x, 2 ) + Math. pow ( y, 2 ))
3.
构成
函数
BiFunction
<
Integer, Integer, Integer
>
pythagorean
=
(
x, y
)
->
( int ) ( Math. sqrt ( Math. pow ( x, 2 ) + Math. pow ( y, 2 ))) ;
( int ) ( Math. sqrt ( Math. pow ( x, 2 ) + Math. pow ( y, 2 ))) ;
4.
函数调用
pythagorean.apply
(
3
,
4
)
- 抽象出有公共法则(数据处理方式)、输入、输出的方法函数化放在工具类中,供各个模块调用
Lambada 可扩展
- 如果已有的函数式接口不够用,可以自定义:
@FunctionalInterface
public interface TriFunction<A, B, C, R> {
R apply(A a, B b, C c);
}
Lambada 复合-关联词
- 比较器复合 Comparator.comparing
- 逆序 reversed()
- 比较器链 thenComparing
- 谓词复合Predicate
- 非negate
- 且and 优先级从左到右
- 或or
- 函数复合Function
- 然后f.andThen(g)=g(f(x))
- 组合f.compose(g)=f(g(x))
Stream 流
What’S Stream?
- 定义:从支持数据处理操作的源生成的元素序列
- 元素序列:流提供类似集合的可以访问特定元素类型的接口;集合->数据,流->计算
- 源:流会使用一个提供数据的源,如集合、数组或输入/输出资源;顺序性
- 数据处理操作:类似数据库操作,如filter、map、reduce、match、sort;执行:顺序、并行
- 特点
- 流水线:数据库查询
- 内部迭代
- 用法
- 一个数据源(如集合)来执行一个查询
- 一个中间操作链,形成一条流的流水线
- 一个终端操作,执行流程线,生成结果
- 其他:
- 有状态操作:接受一个流时需要知道先前的历史
Stream-特征
- 只能遍历一次
- 内部迭代
- parallelStream并行流
- 使用前提:无共享可变状态static
- 映射到数值流
- mapToInt
- 转换回对象流
- boxed
- Optional
- OptionalInt
- OptionalLong
- OptionalDouble
Stream-构建
- 由值构建 Stream.of(“Java 8”,”Lambda”,”In”); Stream.empty()
- 由数组创建 Arrays.stream(new int[]{1,2,3,4,5})
- 由文件生成 Files.lines(Paths.get(dir),Charset.defaultCharset())
- 由函数生成:无限流
- 迭代 Stream.iterate(0,n->n+2)
- 生成 Stream.generate(Supplier<T>) 例如Stream.generate(Math::random)
Stream-模式
- 中间操作:
- 筛选
- 切片
- 映射
- 终端操作
- 查找
- 匹配
- 规约
Stream-flatMap
- 中间操作-映射扁平化
Stream-Reduce
- 前提:内部状态有界
- 类型一:
- 入参
- 一个初始值
- 一个BinaryOperation<T> 两个元素合并成新值
- 返回:T
- 类型二:
- 入参:一个BinaryOperation<T>
- 返回:Optional<T>
Stream-Collector
- public interface Collector<T, A, R> {
- Supplier<A> supplier();//建立新的结果集合
- BiConsumer<A, T> accumulator();//将元素添加到结果容器
- Function<A, R> finisher();//对结果容器应用最终转换
- BinaryOperator<A> combiner();//合并两个结果容器
- Set<Characteristics> characteristics();}//行为:是否可合并规约,优化提示
- UNORDERED 无序
- CONCURRENT 并行,若无UNORDERED,仅无序数据源可以并行规约
- IDENTITY_FINIHSH恒等函数,A不检查转R安全
- T:流中要手机的项目的范型
- A:累加器的类型,在收集过程中用于累积部分结果的对象
- R:收集操作的到的对象的类型
- 功能:
- 将流元素规约和汇总为一个值
- 统计Long:counting()
- 最大值Optional<T>:maxBy(Comparator<T>)
- 总和Integer:summingInt(ToIntFunction<T>)
- 平均Double:averagingInt(ToIntFunction <T>)
- 汇总IntSummaryStatistics:最大,最小,总和,平均: summarizingInt(ToIntFunction <T>)
- 连接字符串String:joining()、 joining(”,”)
- 广义上规约:reducing(U,Function<T,U>,BinaryOperator<U>)【初始值、转换函数;必须:累计函数】
- 元素分组(多级别)Map<K,List<T>>:groupingBy(Function<T,K>,Collector)【必须:Function<T,K>】
- 元素分区Map<Boolean,List<T>>:由一个谓词作为分类函数为分区函数partitioningBy(Predicate<T>,Collector)【必须:Predicate<T>】
- 将流元素规约和汇总为一个值
Stream-Collector-分组
- 多级:collectingAndThen
并行数据处理
Why parallel?
- 快
//影响所有并行流,并行线程池大小,建议设置成处理器数量
log.info("java.util.concurrent.ForkJoinPool.common.parallelism :{}", System.getProperty("java.util.concurrent.ForkJoinPool.common.parallelism"));
Long start = System.nanoTime();
//122
log.info("{}", Stream.iterate(1L, i -> i + 1).limit(1000000).reduce(0L, Long::sum));
long mid = System.nanoTime();
//22
log.info("parallel {}", Stream.iterate(1L, i -> i + 1).limit(1000000).parallel().reduce(0L, Long::sum));
long end = System.nanoTime();
log.info("normal time: {}, parallel time: {}", (mid - start) / 1000000, (end - start) / 10000000);
How parallel?
- 避免共享可变状态
- 想使用并行流提升性能的建议:
- 如有疑问,测量
- 留意装箱,用IntStream等避免装箱
- 有些操作不适合并行如limit,findFirst
- 流水线总成本,单元素通过流水线的处理成本大,并行性能可能更好
- 元素过少时,并行化开销高于处理少数几个元素开销。
- 流自身特点 ,若流可分可并行,若不可分入无限流Distinct就不合适
- 合并的代价 combiner大可能会超出通过并行流得到的性能提升
RecursiveTask-分支/合并框架
- 实现唯一的抽象方法R compute()
- 实现伪代码
ForkJoinPool
- 问题:在处理有共享可变状态问题时,parallel计算结果不正确
- 解决方案:自定义并行流计算Function:自定义ForkJoinTask<R>通过extend RecursiveTask<R>,在其compute()方法中分支/合并算法定义流的拆分、合并,并通过ForkJoinPool.invoke。
- ForkJoinPool,参数:java.util.concurrent.ForkJoinPool.common.parallelism,默认处理器数量
- 并行计算时有成本的,一般要用测试数据说明是否适合使用
Spliterator-Splitable iterator
- 问题:默认拆分粒度可能导致结果并非预期
- 原理:递归调用trySplit生成新Spliterator对象,直到对象为null
- 特性:
自定义Spliterator
- Implements Spliterator<R>实现
- tryAdvance 若剩余元素存在,执行给定操作
- trySplit 拆分子类
- estimateSize 遍历所遇到的元素数量的估量值
- characteristics定义特征
- StreamSupport.stream(自定义spliterator,是否并行)
- 对流进行reduce,初始化、拆分、合并。
CompletableFuture 组合式异步编程
- 场景:异步服务调用
- 方式:
- 用CompletableFuture的compute方法预存储异步线程计算结果;主线程继续执行操作,在需要使用结果时get
- 使用CompletableFuture.supplyAsync(Supplier<U>,Executor),后续join。其中Executor的核心线程数可以根据异步任务数灵活调整
- 复杂的异步调用:构造同步,操作异步;有多个步骤:
- supplyAsync
- thenApply(Function<T,U>)
- thenCompose(Function<T,CompletableFuture>)后join
- 异步组合构造:
- supplyAsync
- thenCombine(supplyAsync,Function<T,U>)后get
复制流
其他新特性
- Optional取代null(10)
- 时间:LocalDate、LocalDateTime、LocalTime、Instant、Duration(12)
------------------------------------------------
| LocalDate | LocalTime | ZoneId |
------------------------------------------------
| LocalDateTime | |
------------------------------------------------
| ZoneDateTime |
------------------------------------------------
思想
- Stream API
- 声明性-更简洁,更易读
- 范型:上下界-类型推断,sort,定义入参要满足的条件
- 可复合-更灵活
- Lambda
- 中间操作、终端操作
- 可并行-性能更好
- parallelStream
- 怎么做-->做什么
- Java7 的sort VS Java8 sorted
方法|异同 | java版本 | 对象 | 性质 | 入参 | 复合 | 返回 |
sort | 7 | list | 终端操作 | comparator | 支持 | void |
sorted | 8 | stream | 中间操作 | comparator | 支持 | stream |
- Stream reduce VS 中间操作,两个入参;Collectors.reducing 终端操作聚合,三个入参;规约,最后一个入场相同且必须
- Collectors VS final修饰的工具类,内部类CollectorImpl实现了Collector 接口