Java8【有与无】【精华篇】实用通识

序:本篇内容较多,排版有些乱,希望大家理解


目录

关键词

行为参数化

方法引用

流 Stream/parallelStream【构建器模式】

分支步骤

默认方法

(结构)模式匹配【不完全支持】

行为参数化【工厂模式】

Lambda表达式

收集器【静态工厂 Collectors】【工厂方法】

 并行处理

 分支/合并框架

 重构、测试和调试

默认方法 

Optional 

CompletableFuture 

新的日期和时间API

参考表格

Github

附录

A.术语

1.并发与并行

2.谓词 Predicate

3.外部迭代与内部迭代

4.Collection与Stream

5.匿名类

6.函数描述符

7.装箱与拆箱 boxing and unboxing

8.实例变量、局部变量和类变量

9.闭包

10.堆与栈

11.集合与流

12.map与flatMap

13. findFirst与 findAny

14.collect、reducing、reduce

15.可读性

16.Lambda表达式特点

17.抽象类和抽象接口之间的区别

18.null 引用和 Optional.empty()

19.混聚mash-up

20.并发与并行

21.同步API与异步API

22.调整线程池的大小

23.并行——使用流还是 CompletableFutures

B.优化与拓展

无副作用计算

声明式编程

函数式编程

数学函数

纯粹的函数式编程

引用透明性

尾-调优化(tail-call optimization)Java8 目前不支持

一等函数(first-class function)

科里化

持久化数据结构

Stream延迟计算

模式匹配 Java8 目前不支持

结合器

C.参考代码

1.Collector接口实现

 2.CompletableFuture

3.拓展 


关键词

 

行为参数化

把方法(你的代码)作为参数传递给另一个方法的能力

 

方法引用

符号  ‘::

(数学)函数作为参数传递给方法,参数可以理解为

函数:无可变共享状态,可以有效、安全地并行执行

类别:

1.指向静态方法的方法引用

2.指向任 意类型实例方法的方法引用 

3.指向现有对象的实例方法的方法引用

// Lambda表达式的签名与函数描述符兼容
List str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
str.sort(String::compareToIgnoreCase);

Function stringToInteger = (String s) -> Integer.parseInt(s);
Function stringToInteger = Integer::parseInt;

BiPredicate, String> contains = (list, element) -> list.contains(element);
BiPredicate, String> contains = List::contains;
// 构造函数引用
// 无参
Supplier c1 = Apple::new;
Apple a1 = c1.get();
// 等价于
Supplier c1 = () -> new Apple();
Apple a1 = c1.get();
// 有参
Function c2 = Apple::new;
Apple a2 = c2.apply(110);
// 等价于
Function c2 = (weight) -> new Apple(weight);
Apple a2 = c2.apply(110);

 

流 Stream/parallelStream【构建器模式】

比喻:车间流水线式工作

特点:透明地并行处理,高级迭代器

定义从支持数据处理操作的源生成的元素序列

操作:中间操作{连接起来的流操作}    终端操作{关闭流的操作}

应用场景:IO密集,CPU密集

对比:

java.util.stream.Stream 串行执行

java.util.Collection.parallelStream  多核架构并行执行 【ForkJoinPool】【慎用

函数

1.reduce 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值。

考虑使用parallelStream
1.是否需要并行?  
2.任务之间是否是独立的?是否会引起任何竞态条件?  
3.结果是否取决于任务的调用顺序?
Stream API带来的代码特性
1.声明性——更简洁,更易读
2.可复合——更灵活
3.可并行——性能更好
使用流
1.一个数据源(如集合)来执行一个查询
2.一个中间操作链,形成一条流的流水线
3.一个终端操作,执行流水线,并能生成结果
// 举例
long count = list.stream()
                 .filter(d -> d.getCalories() > 300)
                 .distinct()
                 .limit(3)
                 .count();

List names = list.stream()
                         .filter(d -> d.getCalories() > 300)
                         .map(Dish::getName)
                         .limit(3)
                         .collect(toList());
// 归约:归类、折叠(folk), 反复迭代
// 有初始值                     初始值,BinaryOperator
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 无初始值                                      BinaryOperator
Optional sum = numbers.stream().reduce((a, b) -> (a + b));
// 数值流
// IntStream、DoubleStream、LongStream,分别将流中的元素特化为 int、long、double, 避免了暗含的装箱成本

// 映射到数值流
//  mapToInt、mapToDouble、mapToLong
IntStream intStream = intList.stream().mapToInt(Dish::getValue);

// 转换回对象流
Stream stream = intStream.boxed();

// 默认值
// Optional 原始类型特化版本: OptionalInt、OptionalDouble、OptionalLong


// 数值范围
// Java 8引入了两个可以用于 IntStream 和 LongStream 的静态方法
IntStream evenNumbers = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0);

// 由值创建流
Stream stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
Stream emptyStream = Stream.empty();

// 由数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();

// 由文件生成流
long uniqueWords = 0;
try(Stream lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                       .distinct()
                       .count();
}
catch(IOException e){
}

// 由函数生成流:创建无限流
// Stream API提供了两个静态方法来从函数生成流: Stream.iterate 和 Stream.generate
// 迭代
Stream.iterate(0, n -> n + 2)
      .limit(10)
      .forEach(System.out::println);
// 生成
Stream.generate(Math::random)
      .limit(5)
      .forEach(System.out::println);

分支步骤

完成一个任务,将其分解成若干个任务,并行处理,然后合并,最终将任务完成

 

默认方法

符号 ‘default

扩充现有接口不影响实现类的代码;

接口中提供默认的实现方法

 

(结构)模式匹配【不完全支持

f(0) = 1
f(n) = n*f(n-1) otherwise

 

行为参数化【工厂模式

应对频繁变更的需求,灵活,简洁

让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为

是类、匿名类、Lambda的集合体

 

Lambda表达式

可以理解成是一种匿名类,或是一种接口的实现类

代码更加简洁

一种编程风格

 用处

1.函数式接口

只定义一个抽象方法的接口,用来传递行为

接口设计 @FunctionalInterface

1.1.Predicate【谓语】【测试者】

java.util.function.Predicate 接口定义了一个名叫 test 的抽象方法,它接受泛型 T 对象,并返回一个 boolean 。

@FunctionalInterface
public interface Predicate{
    boolean test(T t);
}
public static  List filter(List list, Predicate p) {
    List results = new ArrayList<>();
    for(T s: list){
        if(p.test(s)){
            results.add(s);
        }
    }
    return results;
}
Predicate nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

1.2.Consumer【消费者】

java.util.function.Consumer 定义了一个名叫 accept 的抽象方法,它接受泛型 T 的对象,没有返回( void )。你如果需要访问类型 T 的对象,并对其执行某些操作,就可以使用这个接口。T 只能是引用类型。

@FunctionalInterface
public interface Consumer{
    void accept(T t);
}
public static  void forEach(List list, Consumer c){
    for(T i: list){
        c.accept(i);
    }
}

forEach(Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i));

1.3.Function【功能】【类型转换】

java.util.function.Function 接口定义了一个叫作 apply 的方法,它接受一个泛型 T 的对象,并返回一个泛型 R 的对象

@FunctionalInterface
public interface Function{
    R apply(T t);
}
public static  List map(List list, Function f) {
    List result = new ArrayList<>();
    for(T s: list){
        result.add(f.apply(s));
    }
    return result;
}

// [7, 2, 6]
List l = map(Arrays.asList("lambdas","in","action"), (String s) -> s.length());

1.4.Supplier【生产者】

java.util.function.Supplier接口接口定义了一个叫作 get 的方法,它接受一个泛型 T 的对象,并返回一个泛型 T 的对象

@FunctionalInterface
public interface Supplier {
    T get();
}

Supplier supplier = String::new;

 类型检查过程

1.确定目标函数类型

2.执行目标函数

③ 限制条件

1.局部变量必须显式声明为 final,保存在栈上。(产生原因是多线程,本质原因是存储的位置不同)

 

收集器【静态工厂 Collectors】【工厂方法

功能:

1.将流元素归约和汇总为一个值

2.元素分组

3.元素分区

/** 一下是Collectors.reducing 工厂方法提供的广义归约收集器的特殊情况 **/

// 归约collect,计算总数
long howManyDishes = menu.stream().collect(Collectors.counting());
long howManyDishes = menu.stream().count();

// 查找流中的最大值和最小值
Comparator dishValuesComparator = Comparator.comparingInt(Dish::getValues);
Optional mostValuesDishMax = 
    menu.stream().collect(Collectors.maxBy(dishCaloriesComparator));
Optional mostValueDishMin = 
    menu.stream().collect(Collectors.minBy(dishCaloriesComparator));

// 汇总
int totalValues = 
  menu.stream().collect(Collectors.summingInt(Dish::getValues));
double totalValues = 
  menu.stream().collect(Collectors.summingDouble(Dish::getValues));
long totalValues =
  menu.stream().collect(Collectors.summingLong(Dish::getValues));

// 计算数值的平均数
int avgValues = 
  menu.stream().collect(Collectors.averagingInt(Dish::getValues));
double avgValues = 
  menu.stream().collect(Collectors.averagingDouble(Dish::getValues));
long avgValues = 
  menu.stream().collect(Collectors.averagingLong(Dish::getValues));

// 利用summarizingInt工厂方法返回的收集器,一次性获取总和、平均值、最大值和最小值
IntSummaryStatistics statistics = 
  menu.stream().collect(Collectors.summarizingInt(Dish::getValues));
DoubleSummaryStatistics statistics = 
  menu.stream().collect(Collectors.summarizingDouble(Dish::getValues));
LongSummaryStatistics statistics = 
  menu.stream().collect(Collectors.summarizingLong(Dish::getValues));

// 连接字符串,内部使用了StringBuilder
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

// 一般通用的归约, 要用Collectors.reducing工厂方法进行归约
int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));
// stream->collect->reducing->用法介绍,用于可变的归约,适合并行操作
// reducing收集器永远都不会返回 Optional.empty() 

// 无初始值的情况,返回一个可以生成Optional结果的Collector
// 参数:op是核心函数,作用是如何处理两个变量。
//          第一个变量是累积值,可以理解为sum,第二个变量则是下一个要计算的元素。
// 类型:T是Stream里的元素类型
// public static  Collector>reducing(BinaryOperator op)

Comparator byHeight = Comparator.comparing(Person::getHeight);
Map> tallestByCity = personList.stream().collect(
  groupingBy(
    Person::getCity, reducing(BinaryOperator.maxBy(byHeight))
  )
);
tallestByCity.forEach(
  (k,v)->
    System.out.println(
      JSONObject.fromObject(k) + "=" + JSONObject.fromObject(v.get())
    )
);

// 有初始值的情况,返回一个可以直接产生结果的Collector
// 参数:identity[返回类型的初始值]
//      op是核心函数,作用是如何处理两个变量。
//        第一个变量是累积值,可以理解为sum,第二个变量则是下一个要计算的元素。
// 类型:T是Stream里的元素类型
// public static  Collector>reducing(T identity,BinaryOperator op

Map tallestByCity1 = personList.stream().collect(
  groupingBy(Person::getCity, reducing(new Person(), BinaryOperator.maxBy(byHeight)))
);
tallestByCity1.forEach((k,v)->
  System.out.println(JSONObject.fromObject(k) + "=" + JSONObject.fromObject(v)));

// 有初始值,还有针对元素的处理方案mapper,生成一个可以直接产生结果的Collector,
// 元素在执行结果操作op之前需要先执行mapper进行元素转换操作
// 参数:
//      identity[返回类型的初始值]
//      mapper则是map的作用,意义在于将Stream流转换成你想要的类型流
//      op是核心函数,作用是如何处理两个变量。
//        第一个变量是累积值,可以理解为sum,第二个变量则是下一个要计算的元素。
// 类型:U 返回类型;T是Stream里的元素类型;
// public static Collector reducing(U identity,
// Function mapper,BinaryOperator op)

Comparator byLength = Comparator.comparing(String::length);
Map longestNameByCity = personList.stream().collect(
  groupingBy(
    Person::getCity, reducing("", Person::getLastName, BinaryOperator.maxBy(byLength))
  )
);
longestNameByCity.forEach((k,v)->
  System.out.println(JSONObject.fromObject(k)  + "=" + v));

// 有初始值,迭代计算,返回结果值
// 参数:
//   identity[返回类型的初始值]
//   accumulator[迭代计算] 
// 类型:T是Stream里的元素类型
// T reduce(T identity, BinaryOperator accumulator)

Integer sums = Stream.of(1, 2, 3, 4).reduce(0, (sum, item) -> sum + item);
        System.out.println(sums);

 

// 分组,groupingBy,桶
Map> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
// 结果
// {FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza],
// MEAT=[pork, beef, chicken]}

public enum CaloricLevel { DIET, NORMAL, FAT }
Map> 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;
    } )
);

// 多级分组
Map>> dishesByTypeCaloricLevel =
    menu.stream().collect(
        groupingBy(Dish::getType, // 一级分组
            groupingBy(dish -> { // 二级分组
                if (dish.getCalories() <= 400) return CaloricLevel.DIET;
                else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
                else return CaloricLevel.FAT;
            })
        )
);
// 结果
// {MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]},
// FISH={DIET=[prawns], NORMAL=[salmon]},
// OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}

Map typesCount = 
    menu.stream().collect(groupingBy(Dish::getType, counting()));
// 结果
// {MEAT=3, FISH=2, OTHER=4}

// 把收集器的结果转换为另一种类型
Map mostCaloricByType =
    menu.stream().collect(
        groupingBy(
            Dish::getType, 
            collectingAndThen(maxBy(comparingInt(Dish::getCalories)),// 包装后的收集器
            Optional::get // 转换函数,转换包装后的收集器的结果,返回最终的收集器
        )
    )
);
// 结果
// {FISH=salmon, OTHER=pizza, MEAT=pork}

// 联合使用的其他收集器
Map totalCaloriesByType = menu.stream().collect(
  groupingBy(
    Dish::getType, 
    summingInt(Dish::getCalories)
  )
);

Map> caloricLevelsByType = menu.stream().collect(
  groupingBy(
    Dish::getType, 
    mapping(
      dish -> {
        if (dish.getCalories() <= 400) return CaloricLevel.DIET;
        else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
        else return CaloricLevel.FAT; 
      },
      toSet() // 此处可以让你更多控制结果数据
    )
  )
);
// 结果
// {OTHER=[DIET, NORMAL], MEAT=[DIET, NORMAL, FAT], FISH=[DIET, NORMAL]}
// 分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函数。
// 分区函数返回一个布尔值,得到的分组Map的键类型是Boolean,它最多可以分为true是一组,false是一组。

// 简单分区
Map> partitionedMenu = menu.stream().collect(
    partitioningBy(Dish::isVegetarian) // 分区函数
);
// 结果
// {
//   false=[pork, beef, chicken, prawns, salmon],
//   true=[french fries, rice, season fruit, pizza]
// }

// 分区筛选
List vegetarianDishes = menu.stream().filter(Dish::isVegetarian).collect(toList());
// 等价于
List vegetarianDishes = partitionedMenu.get(true);
// 结果
// [french fries, rice, season fruit, pizza]

// 高级应用
Map>> vegetarianDishesByType = menu.stream().collect(
  partitioningBy(
    Dish::isVegetarian,
    groupingBy(Dish::getType) // 进一步处理
  )
);
// 结果
// {false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]},
// true={OTHER=[french fries, rice, season fruit, pizza]}}

Map mostCaloricPartitionedByVegetarian = menu.stream().collect(
  partitioningBy(
    Dish::isVegetarian,
    collectingAndThen(
      maxBy(comparingInt(Dish::getCalories)),
      Optional::get
    )
  )
);
// 结果
// {false=pork, true=pizza}
// Collector 接口介绍
public interface Collector {
  Supplier supplier();
  BiConsumer accumulator();
  BinaryOperator combiner();
  Function finisher();
  Set characteristics();
}
// 参数
// T 是流中要收集的项目的泛型
// A 是累加器的类型,累加器是在收集过程中用于累积部分结果的对象
// R 是收集操作得到的对象(通常但并不一定是集合)的类型

// 过程总览
// 1.原始流会以递归方式拆分为子流,直到定义流是否需要进一步拆分的一个条件为非(如果分布式工作单
// 位太小,并行计算往往比顺序计算要慢,而且要是生成的并行任务比处理器内核数多很多的话就没什么用了
// 2.所有的子流都可以进行并行处理,进行归约。
// 3.使用收集器combiner方法返回的函数,将所有的部分结果两两合并。这时会把原始流每次拆分时
// 得到的子流对应的结果合并起来。

// 自定义接口的实现
public class ToListCollector implements Collector, List>

// 详细介绍
// 前四个方法都会返回一个会被 collect 方法调用的函数
// 第五个方法 characteristics 则提供了一系列特征,也就是一个提示列表,告诉 collect 方法在执行归
// 约操作的时候可以应用哪些优化(比如并行化)

// 1.生产者:[建立新的结果容器] supplier()

// supplier 方法必须返回一个结果为空的 Supplier ,也就是一个无参数函数,在调用时它会创建一个空的
// 累加器实例,供数据收集过程使用

// 举例
public Supplier> supplier() {
  return () -> new ArrayList();
}
public Supplier> supplier() {
  return ArrayList::new;
}

// 2.累加器:[将元素添加到结果容器] accumulator()

// accumulator 方法会返回执行归约操作的函数。
// 当遍历到流中第n个元素时,这个函数执行时会有两个参数:
//   保存归约结果的累加器(已收集了流中的前 n1 个项目),还有第n个元素本身。
// 该函数将返回void,因为累加器是原位更新,即函数的执行改变了它的内部状态以体现遍历的元素的效果。

// 举例
public BiConsumer, T> accumulator() {
  return (list, item) -> list.add(item);
}
public BiConsumer, T> accumulator() {
  return List::add;
}

// 3.转换器:[对结果容器应用最终转换] finisher()

// 在遍历完流后,finisher方法必须返回在累积过程的最后要调用的一个函数,以便将累加器对象转换为
// 整个集合操作的最终结果。

// 举例
public Function, List> finisher() {
  return Function.identity();
}

// 4.完成器/组合器:[合并两个结果容器] combiner() [并行时执行]
// 返回一个供归约操作使用的函数,它定义了对流的各个子部分进行并行处理时,各个子部分归约所得的累
// 加器要如何合并。

// 举例
public BinaryOperator> combiner() {
  return (list1, list2) -> {
     list1.addAll(list2);
     return list1; 
  }
}

5.characteristics()
// 返回一个不可变的 Characteristics 集合,它定义了收集器的行为
// 1) UNORDERED: 归约结果不受流中项目的遍历和累积顺序的影响
// 2) CONCURRENT:  accumulator函数可以从多个线程同时调用,且该收集器可以并行归约流。
//                 如果收集器没有标为UNORDERED,那它仅在用于无序数据源时才可以并行归约。
// 3) IDENTITY_FINISH: 完成器方法返回的函数是一个恒等函数,可以跳过。
//                      这种情况下,累加器对象将会直接用作归约过程的最终结果。

public class ToListCollector implements Collector, List> {
    @Override
    public Supplier> supplier() {
       return ArrayList::new;
    }
    @Override
    public BiConsumer, T> accumulator() {
        return List::add;
    }
    @Override
    public Function, List> finisher() {
        return Function.indentity();
    }
    @Override
    public BinaryOperator> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }
    @Override
    public Set characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
    }
}

// 如果不实现Collector接口
List dishes = menuStream.collect(
    ArrayList::new, // 生产者
    List::add,      // 累加器
    List::addAll    // 完成器/组合器
);
// characteristics是IDENTITY_FINISH。
// 不易读
// 不推荐这种写法
// 自定义收集器,性能一般会有所提升

 并行处理

// 并行数据处理与性能
// 并行流就是一个把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

// 举例
public static long parallelSum(long n) {
  return Stream.iterate(1L, i -> i + 1)
    .limit(n)
    .parallel() // 将流转换为并行流,之后进行的所有操作都并行执行
    // .sequential() // 将流转换为顺序流,之后进行的所有操作都顺序执行
   .reduce(0L, Long::sum);
}

// 解释
// 并行流内部使用了默认的 ForkJoinPool,它默认的线程数量就是你的处理器数量,这个值是由
// Runtime.getRuntime().available-Processors() 得到的。
// 下面是改变线程池大小,这是一个全局设置,影响代码中所有的并行流,强烈建议你不要修改它。
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");

// 并行化过程本身需要对流做递归划分,把每个子流的归纳操作分配到不同的线程,然后把这些操作的结果合
// 并成一个值。在多个内核之间移动数据的代价也可能比你想的要大,所以很重要的一点是要保证在内核中并
// 行执行工作的时间并行流比在内核之间传输数据的时间长。
// **很多情况下不可能或不方便并行化**

// 错用并行流而产生错误的首要原因,就是使用的算法改变了某些共享状态
// 举例
public static long sideEffectSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).forEach(accumulator::add);
    return accumulator.total;
}
public class Accumulator {
    public long total = 0;
    public void add(long value) {
       total += value; // 错误根源是不是一个原子操作,它会改变多个线程共享的对象的可变状态
    } 
}
public static long sideEffectParallelSum(long n) {
    Accumulator accumulator = new Accumulator();
    LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add);
    return accumulator.total;
}
System.out.println("SideEffect parallel sum done in: " +
    measurePerf(ParallelStreams::sideEffectParallelSum, 10_000_000L) +"msecs" );
// 每次执行的结果都不同

// 影响性能因素
// 1)装箱,大大降低性能
// 2)操作本身在并行流上的性能就比顺序流差:limit和findFirst等依赖于元素顺序的操作,并行代价大。
//    可以调用unordered方法来把有序流变成无序流对无序并行流调用limit可能会比单个有序流更高效。
//    前提是顺序不是主要因素
// 3)虑流的操作流水线的总计算成本,N*Q 是对成本的一个粗略的定性估计。
//    N是要处理的元素的总数,Q是一个元素通过流水线的大致处理成本。
//    Q值较高就意味着使用并行流时性能好的可能性比较大。
// 4)对于较小的数据量,尽量选择顺序流,并行流,并行会造成额外开销。
// 5)考虑流背后的数据结构是否易于分解,易分解的适合并行流。
// 6)流自身的特点,以及流水线中的中间操作修改流的方式,都可能会改变分解过程的性能。
// 7)考虑终端操作中合并步骤的代价是大是小。


 分支/合并框架

// 分支/合并框架, Java 7 引入
// 目的: 以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。
// 原理:实现ExecutorService接口,把子任务分配给线程池(称为 ForkJoinPool)中的工作线程。

// RecursiveTask
// 介绍
// 创建 RecursiveTask 的一个子类,用于将任务提交 ForkJoinPool。
//     R 是并行化任务(以及所有子任务)产生的结果类型。
//     如果任务不返回结果,则是 RecursiveAction 类型(当然它可能会更新其他非局部机构)。
// 定义
// protected abstract R compute();
// 逻辑,分治算法
// if (任务足够小或不可分) {
//   顺序计算该任务
// } else {
//   将任务分成两个子任务
//   递归调用本方法,拆分每个子任务,等待所有子任务完成
//   合并每个子任务的结果
// }

// 注意
// ForkJoinPool一般来说把它实例化一次,然后把实例保存在静态字段中,使之成为单例
// Runtime.availableProcessors 的返回值来决定线程池使用的线程数


// 有效使用它的最佳做法
// 1)对一个任务调用 join 方法会阻塞调用方,直到该任务做出结果。因此,有必要在两个子任务的计算
//    都开始之后再调用它。否则,你得到的版本会比原始的顺序算法更慢更复杂,因为每个子任务都必须
//    等待另一个子任务完成才能启动。
// 2)不应该在 RecursiveTask 内部使用 ForkJoinPool 的 invoke 方法。相反,你应该始终直接调
//    用 compute 或 fork 方法,只有顺序代码才应该用 invoke 来启动并行计算。
// 3)对子任务调用 fork 方法可以把它排进 ForkJoinPool 。同时对左边和右边的子任务调用它似乎很
//    自然,但这样做的效率要比直接对其中一个调用 compute 低。这样做你可以为其中一个子任务重用
//    同一线程,从而避免在线程池中多分配一个任务造成的开销。
// 4)调试使用分支/合并框架的并行计算可能有点棘手。特别是你平常都在你喜欢的IDE里面看栈
//    跟踪(stack trace)来找问题,但放在分支/合并计算上就不行了,因为调用 compute
//    的线程并不是概念上的调用方,后者是调用 fork 的那个。
// 5)和并行流一样,你不应理所当然地认为在多核处理器上使用分支/合并框架就比顺序计算快。
//    一个任务可以分解成多个独立的子任务,才能让性能在并行化时有所提升。
//    所有这些子任务的运行时间都应该比分出新任务所花的时间长;
//    一个惯用方法是把输入/输出放在一个子任务里,计算放在另一个里,这样计算就可以和输入/输出同时
//    进行。
//    此外,在比较同一算法的顺序和并行版本的性能时还有别的因素要考虑。就像任何其他Java代码一样,
//    分支/合并框架需要“预热”或者说要执行几遍才会被JIT编译器优化。这就是为什么在测量性能之前跑几
//    遍程序很重要,测试框架就是这么做的。同时,编译器内置的优化可能会为顺序版本带来一些优势。
//    必须选择一个标准,来决定任务是要进一步拆分还是已小到可以顺序求值。

// 工作窃取(work stealing)
/*
在实际应用中,这意味着这些任务差不多被平均分配到 ForkJoinPool 中的所有线程上。
每个线程都为分配给它的任务保存一个双向链式队列,每完成一个任务,就会从队列头上取出下一个任务开始执行。
基于前面所述的原因,某个线程可能早早完成了分配给它的所有任务,也就是它的队列已经空了,而其他的线程还很忙。
这时,这个线程并没有闲下来,而是随机选了一个别的线程,从队列的尾巴上“偷走”一个任务。
这个过程一直继续下去,直到所有的任务都执行完毕,所有的队列都清空。
这就是为什么要划成许多小任务而不是少数几个大任,这有助于更好地在工作线程之间平衡负载。
一般来说,这种工作窃取算法用于在池中的工作线程之间重新分配和平衡任务
*/

// Spliterator【可分迭代器(splitable iterator)】【java8 接口】
// 一种自动机制来为你拆分流的机制,为了并行执行而设计的。
public interface Spliterator {
    // 按顺序一个一个使用 Spliterator 中的元素,并且如果还有其他元素要遍历就返回 true 
    boolean tryAdvance(Consumer action);
    // 可以把一些元素划出去分给第二个 Spliterator (由该方法返回),让它们两个并行处理。
    Spliterator trySplit();
    // 估计还剩下多少元素要遍历,即使不那么确切,也能快速算出来是一个值有助于让拆分均匀一点。
    long estimateSize();
    // 代表 Spliterator 本身特性集的编码
    int characteristics();
}

 重构、测试和调试

// 重构、测试和调试
// 1.重构
//   重构代码,用Lambda表达式取代匿名类
//   用方法引用重构Lambda表达式
//   用Stream API重构命令式的数据处理

// 匿名类和Lambda表达式中的 this 和 super 的含义是不同的
// 在匿名类中, this 代表的是类自身, 能屏蔽包含类的变量
// 在Lambda中, this 代表的是包含类, 不能屏蔽包含类的变量

// 1)匿名类转Lambda表达式的注意点
interface Task{
    public void execute();
}
public static void doSomething(Runnable r){ r.run(); }
public static void doSomething(Task a){ a.execute(); }

doSomething(new Task() {
    public void execute() {
        System.out.println("Danger danger!!");
    }
});
// 使用显式的类型转换来解决这种模棱两可的情况
doSomething((Task)() -> System.out.println("Danger danger!!"));
doSomething((Runnable)() -> System.out.println("Danger danger!!"));

// 2)Lambda转方法引用的注意点
Map> 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;
            }
        )
    );
// 方法引用
Map> dishesByCaloricLevel =
    menu.stream().collect(groupingBy(Dish::getCaloricLevel));

public class Dish{
    public CaloricLevel getCaloricLevel(){
        if (this.getCalories() <= 400) return CaloricLevel.DIET;
        else if (this.getCalories() <= 700) return CaloricLevel.NORMAL;
        else return CaloricLevel.FAT;
    }
}
// 尽量考虑使用静态辅助方法,比如 comparing 、 maxBy。
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
inventory.sort(comparing(Apple::getWeight));
// 尽量考虑通用的归约操作,比如 sum 、 maximum
int totalCalories = menu.stream().map(Dish::getCalories).reduce(0, (c1, c2) -> c1 + c2);
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

// 3)命令式的数据处理转Stream的注意点
// 命令式代码的模式:筛选和抽取
List dishNames = new ArrayList<>();
for(Dish dish: menu){
    if(dish.getCalories() > 300){
        dishNames.add(dish.getName());
    }
}
menu.parallelStream()
    .filter(d -> d.getCalories() > 300)
    .map(Dish::getName)
    .collect(toList());

// 4)增加代码的灵活性
// 4.1)有条件的延迟执行
if (logger.isLoggable(Log.FINER)){
    logger.finer("Problem: " + generateDiagnostic());
}
// 出现的问题
// 日志器的状态(它支持哪些日志等级)通过 isLoggable 方法暴露给了客户端代码。
// 为什么要在每次输出一条日志之前都去查询日志器对象的状态?
public void log(Level level, Supplier msgSupplier)
logger.log(Level.FINER, () -> "Problem: " + generateDiagnostic());

public void log(Level level, Supplier msgSupplier){
    if(logger.isLoggable(level)){
        log(level, msgSupplier.get());
    }
}
// 4.2)环绕执行
String oneLine = processFile((BufferedReader b) -> b.readLine());
String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine());

public static String processFile(BufferedReaderProcessor p) throws IOException {
    try(BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
        return p.process(br);
    }
}
public interface BufferedReaderProcessor {
    String process(BufferedReader b) throws IOException;
}

// 5)使用Lambda 重构面向对象的设计模式
// 5.1)策略模式
// 5.2)模板方法
// 5.3)策略模式
// 5.4)观察者模式
// 5.5)责任链模式
// 5.6)工厂模式

默认方法 

// 默认方法
// 类库设计者,以兼容的方式改进API
public interface Sized {
    int size();
    default boolean isEmpty() {
        return size() == 0;
    }
}

// 默认方法的两种用例:可选方法和行为的多继承
//   可选方法
interface Iterator {
    boolean hasNext();
    T next();
    default void remove() {
        throw new UnsupportedOperationException();
    }
}
//   行为的多继承
public interface Moveable {
    int getX();
    int getY();
    void setX(int x);
    void setY(int y);
    default void moveHorizontally(int distance){
        setX(getX() + distance);
    }
    default void moveVertically(int distance){
        setY(getY() + distance);
    }
}

// 组合接口
public class Monster implements Rotatable, Moveable, Resizable {

}

// 关于继承的一些错误观点
// 继承不应该成为你一谈到代码复用就试图倚靠的万精油。
// 声明为 final 的类不能被其他的类继承,避免发生这样的反模式,防止核心代码的功能被污染。


// 解决冲突的规则
/**
如果一个类使用相同的函数签名从多个地方(比如另一个类或接口)继承了方法,通过三条规则可以进行判断。
(1) 类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
(2) 如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的
默认方法的接口,即如果 B 继承了 A ,那么 B 就比 A 更加具体。
(3) 最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪
一个默认方法的实现。
**/

Optional 

// Java语言的架构师Brian Goetz:
//     Optional 的设计初衷仅仅是要支持能返回 Optional 对象的语法

// 用 Optional 取代 null

// null 带来的种种问题
/**
1.它是错误之源
    NullPointerException 是目前Java程序开发中最典型的异常。
2.它会使你的代码膨胀
    它让你的代码充斥着深度嵌套的 null 检查,代码的可读性糟糕透顶。
3.它自身是毫无意义的
    null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的模。
4.它破坏了Java的哲学
    Java一直试图避免让程序员意识到指针的存在,唯一的例外是: null 指针。
5.它在Java的类型系统上开了个口子
    null 并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,原因是当这个
变量被传递到系统中的另一个部分后,你将无法获知这个 null 变量最初的赋值到底是什么类型。
**/

// 应用 Optional 的几种模式
// 1.创建 Optional 对象

// 1.1.声明一个空的 Optional
Optional optCar = Optional.empty();
// 1.2.依据一个非空值创建 Optional,car对象不能为 null
Optional optCar = Optional.of(car);
// 1.3.可接受 null 的 Optional
Optional optCar = Optional.ofNullable(car);

// 2.使用 map 从 Optional 对象中提取和转换值
String name = null;
if(insurance != null){
    name = insurance.getName();
}
// 等价
Optional optInsurance = Optional.ofNullable(insurance);
Optional name = optInsurance.map(Insurance::getName);

// 使用 flatMap 链接 Optional 对象
public String getCarInsuranceName(Person person) {
    return person.getCar().getInsurance().getName();
}
// 等价
Optional optPerson = Optional.of(person);
Optional name =
    optPerson.flatMap(Person::getCar)
             .flatMap(Car::getInsurance)
             .map(Insurance::getName)
             .orElse("Unknown");// 如果 Optional 的结果值为空,设置默认值

// Optional无法序列化的解决方案
public class Person {
    private Car car;
    public Optional getCarAsOptional() {
        return Optional.ofNullable(car);
    }
}

// 3.Optional的方法
/**
get() 是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个
NoSuchElementException 异常。所以,除非你非常确定 Optional变量一定包含值,否则使用这个方法是个
相当糟糕的主意。此外,这种方式即便相对于嵌套式的 null 检查,也并未体现出多大的改进。

orElse(T other) 允许你在Optional对象不包含值时提供一个默认值。

orElseGet(Supplier other) 是 orElse 方法的延迟调用版, Supplier方法只有在
Optional 对象不含值时才执行调用。如果创建默认值是件耗时费力的工作,你应该考虑采用这种方式(借此提
升程序的性能),或者你需要非常确定某个方法仅在Optional 为空时才进行调用,也可以考虑该方式(这种情
况有严格的限制条件)。

orElseThrow(Supplier exceptionSupplier) 和 get 方法非常类似,它们遭遇 Optional
对象为空时都会抛出一个异常,但是使用 orElseThrow 你可以定制希望抛出的异常类型。

ifPresent(Consumer) 让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何
操作。
**/

// 使用
// 不安全,容易发生null异常
public Optional nullSafeFindCheapestInsurance(
    Optional person, Optional car) {
    if (person.isPresent() && car.isPresent()) {
        // get()容易发生null异常
        return Optional.of(findCheapestInsurance(person.get(), car.get()));
    } else {
        return Optional.empty();
    }
}
// 安全
public Optional nullSafeFindCheapestInsurance(
    Optional person, Optional car) {
    // 如果person为null,则flatMap不会执行;如果car为null,则返回一个空的 Optional 对象
    return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}


// 使用 filter 剔除特定的值
Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
    System.out.println("ok");
}
// 
Optional optInsurance = ...;
optInsurance.filter(insurance ->"CambridgeInsurance".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));

public String getCarInsuranceName(Optional person, int minAge) {
return person.filter(p -> p.getAge() >= minAge)
    .flatMap(Person::getCar)
    .flatMap(Car::getInsurance)
    .map(Insurance::getName)
    .orElse("Unknown");
}

// 优化
// 1.用 Optional 封装可能为 null 的值
Object value = map.get("key");
Optional value = Optional.ofNullable(map.get("key"));

// 2.异常与 Optional 的对比;OptionalUtility工具类
public static Optional stringToInt(String s) {
    try {
        return Optional.of(Integer.parseInt(s));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
} 
  

CompletableFuture 

// CompletableFuture: 组合式异步编程
// 参考文章:https://blog.csdn.net/u012549626/article/details/91572434

// Future 接口: 设计初衷是对将来某个时刻会发生的结果进行建模。易用。Java 5中被引入。
// 它建模了一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个引用被返回给调用方。

// Future 接口的局限性
// 1)将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。
// 2)等待 Future 集合中的所有任务都完成。
// 3)仅等待 Future 集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同一个值),
// 并返回它的结果。
// 4)通过编程方式完成一个 Future 任务的执行(即以手工设定异步操作结果的方式)。
// 5)应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future计算的结果
// 进行下一步的操作,不只是简单地阻塞等待操作的结果)。

//  CompletableFuture实现了Future, CompletionStage两个接口 Java 8中被引入。
// 当一个Future可能需要显示地完成时,使用CompletionStage接口去支持完成时触发的函数和操作。
// 当两个及以上线程同时尝试完成、异常完成、取消一个CompletableFuture时,只有一个能成功。

// (1) CompletableFuture实现了CompletionStage接口的如下策略:
/**
   1) 为了完成当前的CompletableFuture接口或者其他完成方法的回调函数的线程,提供了非异步的完成操作。
   2) 没有显式入参Executor的所有async方法都使用ForkJoinPool.commonPool()为了简化监视、调试和跟踪,所有生成的异步任务都是标记接口AsynchronousCompletionTask的实例。
   3) 所有的CompletionStage方法都是独立于其他共有方法实现的,因此一个方法的行为不会受到子类中其他方法的覆盖
**/

// (2) CompletableFuture实现了Future接口的如下策略:
/**
   1) CompletableFuture无法直接控制完成,所以cancel操作被视为是另一种异常完成形式。方法isCompletedExceptionally可以用来确定一个CompletableFuture是否以任何异常的方式完成。
  2) 以一个CompletionException为例,方法get()和get(long,TimeUnit)抛出一个ExecutionException,对应CompletionException。为了在大多数上下文中简化用法,这个类还定义了方法join()和getNow,而不是直接在这些情况中直接抛出CompletionException。
**/

// CompletableFuture中4个异步执行任务静态方法:
public static  CompletableFuture supplyAsync(Supplier supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}  
public static  CompletableFuture supplyAsync(Supplier supplier,Executor executor) {
    return asyncSupplyStage(screenExecutor(executor), supplier);
}   
public static CompletableFuture runAsync(Runnable runnable) {
    return asyncRunStage(asyncPool, runnable);
}   
public static CompletableFuture runAsync(Runnable runnable, Executor executor) {
    return asyncRunStage(screenExecutor(executor), runnable);
}

// 说明
// 其中supplyAsync用于有返回值的任务,runAsync则用于没有返回值的任务。Executor参数可以手动指
// 定线程池,否则默认ForkJoinPool.commonPool()系统级公共线程池,
// 注意:这些线程都是Daemon线程,主线程结束Daemon线程不结束,只有JVM关闭时,生命周期终止。


// 方法介绍
// 1.创建任务

public static  CompletableFuture supplyAsync(Supplier supplier)
public static  CompletableFuture supplyAsync(Supplier supplier,Executor executor)
public static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable, Executor executor)


// 2.获取执行结果

// 堵塞当前的线程,等待计算结果完成
V get(); 
// 堵塞当前的线程,可以设置等待的时间
V get(long timeout,Timeout unit); 
// 当有了返回结果时会返回结果,如果异步线程抛了异常会返回自己设置的默认值(传入的值).
T getNow(T defaultValue); 
// 获取执行结果,阻塞线程,等待计算结果
T join();

// get与join的区别:
// 抛出未经检查的 CompletionException
V get() throws InterruptedException, ExecutionException;
// 不会抛出已检查的异常,利用exceptionally()截获异常
public T join()

// 举例
try {
  CompletableFuture.allOf(fanoutRequestList).get() 
} catch (InterruptedException | ExecutionException e) {
  e.printStackTrace();
}

CompletableFuture> cf = CompletableFuture
    .supplyAsync(this::process)
    .exceptionally(this::getFallbackListOfStrings) // CompletionException
    .thenAccept(this::processFurther);


// 立即完成计算,并把结果设置为传的值,返回是否设置成功
// 如果 CompletableFuture 没有关联任何的Callback、异步任务等,如果调用get方法,那会一直阻塞下
// 去,可以使用complete方法主动完成计算
public boolean complete(T value)
// 立即完成计算,并抛出异常
public boolean completeExceptionally(Throwable ex) 


// 3.当前任务正常完成以后执行,当前任务的执行结果可以作为下一任务的输入参数,无返回值.
public CompletionStage thenAccept(Consumer action);
public CompletionStage thenAcceptAsync(Consumer action);
public CompletionStage thenAcceptAsync(Consumer action,Executor executor);

// 4.对不关心上一步的计算结果,执行下一个操作
public CompletionStage thenRun(Runnable action);
public CompletionStage thenRunAsync(Runnable action);
public CompletionStage thenRunAsync(Runnable action,Executor executor);

// 5.当前任务正常完成以后执行,当前任务的执行的结果会作为下一任务的输入参数,有返回值
public  CompletableFuture     thenApply(Function fn)
public  CompletableFuture     thenApplyAsync(Function fn)
public  CompletableFuture     thenApplyAsync(Function fn, Executor executor)

// 6.结合两个CompletionStage的结果,进行转化后返回
// thenCombine(..)  thenAcceptBoth(..)  runAfterBoth(..)
public  CompletableFuture thenCombine(CompletionStage other, BiFunction fn)
public  CompletableFuture     thenCombineAsync(CompletionStage other, BiFunction fn)
public  CompletableFuture thenCombineAsync(CompletionStage other, BiFunction fn, Executor executor)

// 7.这个方法接收的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CompletableFuture
public  CompletableFuture thenCompose(Function> fn)
public  CompletableFuture thenComposeAsync(Function> fn)
public  CompletableFuture thenComposeAsync(Function> fn, Executor executor)

// thenApply与thenCompose区别
thenApply:消费者,功能转换
thenCompose:用来连接两个CompletableFuture,返回值是一个新的CompletableFuture

public  CompletableFuture thenApply(
  Function fn) {
  return uniApplyStage(null, fn);
}

public  CompletableFuture thenCompose(
  Function> fn) {
  return uniComposeStage(null, fn);
}

// 8.执行两个CompletionStage的结果,那个先执行完了,就是用哪个的返回值进行下一步操作
// applyToEither(..)  acceptEither(..)  runAfterEither(..)
public  CompletionStage applyToEither(CompletionStage other,Function fn);
public  CompletionStage applyToEitherAsync(CompletionStage other,Function fn);
public  CompletionStage applyToEitherAsync(CompletionStage other,Function fn,Executor executor);

// 9.当运行出现异常时,调用该方法可进行一些补偿操作,如设置默认值.
public CompletionStage exceptionally(Function fn);

// 10.当CompletableFuture的计算结果完成,或者抛出异常的时候,都可以进入whenComplete方法执行
public CompletionStage whenComplete(BiConsumer action);
public CompletionStage whenCompleteAsync(BiConsumer action);
public CompletionStage whenCompleteAsync(BiConsumer action,Executor executor);

// 11.当CompletableFuture的计算结果完成,或者抛出异常的时候,可以通过handle方法对结果进行处理
public  CompletionStage handle(BiFunction fn);
public  CompletionStage handleAsync(BiFunction fn);
public  CompletionStage handleAsync(BiFunction fn,Executor executor);

// 12.allOf、anyOf
// allOf:当所有的CompletableFuture都执行完后执行计算
// anyOf:最快的那个CompletableFuture执行完之后执行计算

新的日期和时间API

参考表格

Github

 

附录

A.术语

1.并发与并行

并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

2.谓词 Predicate

数学上,常用来代表一个类似函数的东西,接受一个参数值,并返回 true 或 false

3.外部迭代与内部迭代

外部迭代是手动代码迭代,可以迭代过程,需要自己去迭代

内部迭代是迭代过程逻辑不可见,但是可以理解,不需要自己去迭代

Java8【有与无】【精华篇】实用通识_第1张图片

4.Collection与Stream

Collection主要是存储和访问数据

Stream则主要对数据的计算,并行计算

5.匿名类

允许你同时声明并实例化一个类,随用随建

占用较多的内存空间

6.函数描述符

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

7.装箱与拆箱 boxing and unboxing

装箱:将原始类型转换为对应的引用类型的机制,保存到中。Predicate 可以避免装箱

拆箱:将引用类型转换为对应的原始类型

8.实例变量、局部变量和类变量

本质不同:实例变量都存储在堆中,局部变量保存在栈上,类变量保存方法区中

实例变量:定义在类中的变量成为实例变量,又称类的成员变量。具有默认的初始值,从属于类由类生成对象时,才分配存储空间,各对象间的实例变量互不干扰,能通过对象的引用来访问实例变量。java多线程中,实例变量是多个线程的共享资源,在同步访问时可能出现的问题。

类变量:类变量也称静态变量,用static关键字修饰。一个类的静态变量,所有由这类生成的对象都共用这个类变量,类装载时就分配存储空间。一个对象修改了变量,则所以对象中这个变量的值都会发生改变。

局部变量:局部变量是方法中或者局部块中声明定义的变量或方法,没有默认初始值,赋值后才能使用

9.闭包

用科学的说法来说,闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量。

可以认为Lambda是对值封闭,而不是对变量封闭。

10.堆与栈

栈是在线程之间独立的

函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
栈内存中的数据,没有默认初始化值,需要手动设置。

堆是在线程之间共享的

堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
堆内存中的实体是用来封装数据的,这些数据都有默认初始化值。
堆内存中的实体不再被指向时,JVM启动垃圾回收机制,自动回收。

 

【待续】JAVA在程序运行时,在内存中划分5片空间进行数据的存储。分别是:寄存器、本地方法区、方法区、栈、堆【待续】

11.集合与流

集合【急切创建】

定义一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。

特点:多次遍历

流【延迟创建】

定义在概念上固定的数据结构,其元素则是按需计算的。

特点:只能遍历一次

12.map与flatMap

map 映射成一个流

flatMap 映射成流的内容,把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流

List words = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List b =
    words.stream()
         .map(w -> w.split(""))
         .collect(Collectors.toList());

List a =
    words.stream()
         .map(w -> w.split(""))
         .flatMap(Arrays::stream)
         .collect(Collectors.toList());

13. findFirst与 findAny

findFirst 返回第一个元素,出现逻辑顺序时用,并行限制多

findAny 返回当前流中的任意元素,不在乎顺序,并行限制少

14.collect、reducing、reduce

reduce 根据一定的规则将Stream中的元素进行计算后返回一个唯一的值,终端操作(有状态-有界)。适合不可变容器归约

collect 收集器,把流归约成一个集合,终端操作。适合可变容器归约,适合并行

reducing 是一个收集器的归约操作,参数为BinaryOperator,使用场景如下:

// 错误写法
Stream stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
List numbers = stream.reduce(
        new ArrayList(),
        (List l, Integer e) -> {
            l.add(e);
            return l; 
        }, 
        (List l1, List l2) -> {
            l1.addAll(l2);
            return l1; 
        }
);

15.可读性

别人理解这段代码的难易程度

16.Lambda表达式特点

  1. 行为参数化,表示不同的行为
  2. 函数接口
  3. 重构代码带来的灵活行,有条件的延迟执行和环绕执行
  4. 简洁
  5. 易懂,配合方法引用

17.抽象类和抽象接口之间的区别

一个类只能继承一个抽象类,但是一个类可以实现多个接口。

一个抽象类可以通过实例变量(字段)保存一个通用状态,而接口是不能有实例变量的。

18.null 引用和 Optional.empty()

从语义上,它们是一回事儿。

本质的区别:

解引用一个null, 一 定会触 发 NullPointerException;

使用Optional.empty() 正常处理,它是 Optional 类的一个有效对象;

19.混聚mash-up

使用来自多个来源的内容,将这些内容聚合在一起,方便用户的生活。

20.并发与并行

Java8【有与无】【精华篇】实用通识_第2张图片

21.同步API与异步API

同步API[阻塞式调用]:你调用了某个方法,调用方在被调用方运行的过程中会等待,被调用方运行结束返回,调用方取得被调用方的返回值并继续运行。使调用方和被调用方在不同的线程中运行,调用方还是需要等待被调用方结束运行。


异步API[非阻塞式调用]:会直接返回。在被调用方计算完成之前,将它剩余的计算任务交给另一个线程去做,该线程和调用方是异步的。

22.调整线程池的大小

Brian Goetz建议,线程池大小与处理器的利用率之比可以使用下面的公式进行估算:
Java8【有与无】【精华篇】实用通识_第3张图片

建议你将执行器使用的线程数,与你需要查询的商店数目设定为同一个值,这样每个商店都应该对应一个服务线程。

23.并行——使用流还是 CompletableFutures

1.如果你进行的是计算密集型的操作,并且没有I/O,那么推荐使用 Stream 接口,因为实现简单,同时效率也可能是最高的

(如果所有的线程都是计算密集型的,那就没有必要创建比处理器核数更多的线程)。

2.如果你并行的工作单元还涉及等待I/O的操作(包括网络连接等待),那么使用CompletableFuture 灵活性更好,你可以像前

文讨论的那样,依据等待/计算,或者W/C的比率设定需要使用的线程数。这种情况不使用并行流的另一个原因是,处理流的流

水线中如果发生I/O等待,流的延迟特性会让我们很难判断到底什么时候触发了等待。

24.Spliterator与Iterator

Spliterator为了并行遍历数据源中的元素而设计的迭代器,并行遍历。

Iterator提供的顺序遍历迭代器,顺序遍历。

 

B.优化与拓展

1.将Collection转成Stream,处理后再转成Collection,提高效率,更加简化

无副作用计算

放心地共享它,无需保留任何副本,并且由于它们不会被修改,还是线程安全的。

声明式编程

要做什么:你只需要使用不相互影响的表达式,描述想要做什么,由系统来选择如何实现。

函数式编程

它是一种使用函数进行编程的方式。
如果程序有一定的副作用,调用者不需要知道,或者完全不在意这些副作用,因为这对它完全没有影响。

数学函数

它接受零个或多个参数,生成一个或多个结果,并且不会有任何副作用。

纯粹的函数式编程

无副作用计算,纯数学函数。

"函数式"的函数或方法都只能修改本地变量。除此之外,它引用的对象都应该是不可变对象(final 类型)。

引用透明性

如果一个函数只要传递同样的参数值,总是返回同样的结果。
函数式方法不允许修改任何全局数据结构或者任何作为参数传入的参数。

尾-调优化(tail-call optimization)Java8 目前不支持

迭代调用发生在函数的最后。
尾-递(tail-recursive):只用一个栈帧,实现递归,目前编译器未做该优化。

使用Java 8进行编程时,建议尽量使用 Stream 取代迭代操作,从而避免变化带来的影响。


一等函数(first-class function)

可以作为参数传递,可以作为返回值,还能存储在数据结构中。能够像普通变量一样使用的函数。

高阶函数(higher-order function)
满足下面任一要求
1)接受至少一个函数作为参数
2)返回的结果是一个函数

科里化

它是一种可以帮助你模块化函数、提高代码重用性的技术。
它表示一种将一个带有n元组参数的函数转换成n个一元函数链的方法。——俄国数学家Moses Schönfinkel

理论定义
一种将具备2个参数的函数 f 转化为使用一个参数的函数 g,并且这个函数的返回值也是一个函数,它会作为新函数的一个参数。
后者的返回值和初始函数的返回值相同,即 f(x,y) = (g(x))(y) 。


函数式,不会对原有数据结构进行改动。但可能会有很多副本的存在。

持久化数据结构

通过函数式方法/编程,保证数据结构的值始终保持一致,即,创建副本。
前提是不能修改数据源(final)。

Stream延迟计算

当你向一个 Stream发起一系列的操作请求时,这些请求只是被一一保存起来。只有当你向Stream发起一个终端操作时,才会实际地进行计算。

模式匹配 Java8 目前不支持

Java中有switch,但是有限制byte、short、int 或者 char,String(java7引入)

Scala语言:一种混合了面向对象和函数式编程的语言
object Test {
   def main(args: Array[String]) {
       val alice = new Person("Alice", 25)
    val bob = new Person("Bob", 32)
       val charlie = new Person("Charlie", 32)
   
    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // 样例类
   case class Person(name: String, age: Int)
}

结合器

高阶函数接受两个或多个函数,并返回另一个函数,实现的效果在某种程度上类似于将这些函数进行了结合。

 

C.参考代码

1.Collector接口实现

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.IntStream;

import static java.util.stream.Collector.Characteristics.IDENTITY_FINISH;

public class PrimeNumbersCollector
    implements Collector>, Map>> {

  public static void main(String[] args) {
    PrimeNumbersCollector primeNumbersCollector = new PrimeNumbersCollector();
    int n = 9;
    Map> result = IntStream.rangeClosed(2, n).boxed()
        .collect(primeNumbersCollector);

    result.forEach((k1,v1)->v1.forEach(v2->System.out.println(v2)));

    System.out.println("--------------------------");
    result = primeNumbersCollector.partitionPrimesWithCustomCollector(9);
    result.forEach((k1,v1)->v1.forEach(v2->System.out.println(v2)));

  }

  // 除了实现接口外,还可以实现
  public Map> partitionPrimesWithCustomCollector(int n) {
    return IntStream.rangeClosed(2, n).boxed()
      .collect(
          () -> new HashMap>(2) {{
            put(true, new ArrayList<>());
            put(false, new ArrayList<>());
          }},
          (acc, candidate) -> {
            acc.get(isPrime(acc.get(true), candidate) )
                .add(candidate);
          },
          (map1, map2) -> {
            map1.get(true).addAll(map2.get(true));
            map1.get(false).addAll(map2.get(false));
          });
  }

  /**
   * 将找到的质数添加已经找到的质数列表
   * @param sourceList 已经找到的质数列表
   * @param candidate 判断是否是质数
   * @return boolean true质数,false非质数
   * **/
  private boolean isPrime(List sourceList, int candidate) {
    int candidateRoot = (int) Math.sqrt((double) candidate);
    if (IntStream.rangeClosed(2, candidateRoot).noneMatch(i -> candidate % i == 0)) {
     return false;
    }
    return true;
  }

  @Override
  public Supplier>> supplier() {
    return () -> new HashMap>() {{
      put(true, new ArrayList<>());
      put(false, new ArrayList<>());
    }};
  }
  @Override
  public BiConsumer>, Integer> accumulator() {
    return (Map> acc, Integer candidate) -> {
      acc.get(isPrime(acc.get(true), candidate)).add(candidate);
    };
  }
  @Override
  public BinaryOperator>> combiner() {
    return (Map> map1,
            Map> map2) -> {
      map1.get(true).addAll(map2.get(true));
      map1.get(false).addAll(map2.get(false));
      return map1;
    };
  }
  @Override
  public Function>,
      Map>> finisher() {
    return Function.identity();
  }
  @Override
  public Set characteristics() {
    return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
  }
}

 2.CompletableFuture

import java.util.Random;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Function;

public class Main {
  public static void main(String[] args) {
    try{
      //futureTest();

      //runAsync();
      //supplyAsync();

      //whenComplete();
      //whenComplete2();

      //thenAcceptTest();
      //thenRunTest();
      //thenApplyTest();
      //thenCombineTest();
      //thenAcceptBothTest();
      //runAfterBothTest();

      //thenComposeTest();

      //applyToEitherTest();
      //acceptEitherTest();

      //exceptionallyTest();

      //handleTest();

      allOfAndAnyOfTest();
    }catch (Exception ex) {
      System.out.println(ex);
    }
  }

  public static void futureTest() {
    try{
      // CompletableFuture实现了Future接口,因此你可以像Future那样使用它。
      //其次,CompletableFuture并非一定要交给线程池执行才能实现异步,你可以像下面这样实现异步运行:
      //如果发生异常:异常会被限制在执行任务的线程的范围内,最终会杀死该线程,而这会导致等待get方法返回,结果的线程永久地被阻塞.
      //[解]客户端可以使用重载版本的get 方法,它使用一个超时参数来避免发生这样的情况。这是一种值得推荐的做法,你应该尽量在你的代码中添加超时判断的逻辑,避免发生类似的问题。
      CompletableFuture completableFuture = new CompletableFuture<>();
      new Thread(() -> {
        // 模拟执行耗时任务
        System.out.println("task doing...");
        try {
          Thread.sleep(3000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        // 告诉completableFuture任务已经完成
        completableFuture.complete("ok");
      }).start();
      // 获取任务结果,如果没有完成会一直阻塞等待
      String result = completableFuture.get();
      System.out.println("计算结果:" + result);
    } catch (Exception e) {
      System.out.println(e);
    }

    // 获取任务线程内发生的异常
    try{
      CompletableFuture completableFuture = new CompletableFuture<>();
      new Thread(() -> {
        // 模拟执行耗时任务
        System.out.println("task doing...");
        try {
          Thread.sleep(3000);
          int i = 1/0;
        } catch (Exception e) {
          // 告诉completableFuture任务发生异常了
          completableFuture.completeExceptionally(e);
        }
        // 告诉completableFuture任务已经完成
        completableFuture.complete("ok");
      }).start();
      // 获取任务结果,如果没有完成会一直阻塞等待
      String result = completableFuture.get();
      System.out.println("计算结果:" + result);
    }catch (Exception ex) {
      System.out.println(ex);
    }
  }

  public static void thenAcceptTest(){
    //当前任务正常完成以后执行,当前任务的执行结果可以作为下一任务的输入参数,无返回值.
    //执行任务A,同时异步执行任务B,待任务B正常返回之后,用B的返回值执行任务C,任务C无返回值
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> "任务A");
    CompletableFuture futureB = CompletableFuture.supplyAsync(() -> "任务B");
    futureB.thenAccept(b -> {
      System.out.println("参数:" + b);
      System.out.println("执行任务C.");
    });
  }

  public static void thenRunTest(){
    //对不关心上一步的计算结果,执行下一个操作
    //执行任务A,任务A执行完以后,执行任务B,任务B不接受任务A的返回值(不管A有没有返回值),也无返回值
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> "任务A");
    futureA.thenRun(() -> System.out.println("执行任务B"));
  }

  public static void thenApplyTest(){
    //当前任务正常完成以后执行,当前任务的执行的结果会作为下一任务的输入参数,有返回值
    //多个任务串联执行,下一个任务的执行依赖上一个任务的结果,每个任务都有输入和输出
    //异步执行任务A,当任务A完成时使用A的返回结果resultA作为入参进行任务B的处理,可实现任意多个任务的串联执行
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> "hello");
    CompletableFuture futureB = futureA.thenApply(s->s + " world");
    CompletableFuture futureC = futureB.thenApply(String::toUpperCase);
    System.out.println(futureC.join());
//    try {
//      System.out.println(futureC.get());
//    } catch (InterruptedException e) {
//      e.printStackTrace();
//    } catch (ExecutionException e) {
//      e.printStackTrace();
//    }
  }

  public static void thenCombineTest(){
    //结合两个CompletionStage的结果,进行转化后返回
    CompletableFuture futurePrice = CompletableFuture.supplyAsync(() -> 100d);
    CompletableFuture futureDiscount = CompletableFuture.supplyAsync(() -> 0.8);
    CompletableFuture futureResult = futurePrice.thenCombine(futureDiscount, (price, discount) -> price * discount);
    System.out.println("最终价格为:" + futureResult.join());
  }

  public static void thenAcceptBothTest(){
    //结合两个CompletionStage的结果,进行转化
    CompletableFuture futurePrice = CompletableFuture.supplyAsync(() -> 100d);
    CompletableFuture futureDiscount = CompletableFuture.supplyAsync(() -> 0.8);
    futurePrice.thenAcceptBoth(futureDiscount, (price, discount) -> {
      System.out.println("最终价格为:" + price * discount);
    });
  }

  public static void runAfterBothTest(){
    //结合两个CompletionStage的结果,进行转化后执行
    CompletableFuture futurePrice = CompletableFuture.supplyAsync(() -> 100d);
    CompletableFuture futureDiscount = CompletableFuture.supplyAsync(() -> 0.8);
    System.out.println(futureDiscount.join());
    futurePrice.runAfterBoth(futureDiscount, new Runnable() {
      @Override
      public void run() {
        System.out.println("开始 run");
      }
    });
  }

  public static void thenComposeTest(){
    //这个方法接收的输入是当前的CompletableFuture的计算值,返回结果将是一个新的CompletableFuture
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> "hello");
    CompletableFuture futureB = futureA.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " world"));
    CompletableFuture futureC = futureB.thenCompose(s -> CompletableFuture.supplyAsync(s::toUpperCase));
    System.out.println(futureC.join());
  }

  public static void applyToEitherTest(){
    //执行两个CompletionStage的结果,那个先执行完了,就是用哪个的返回值进行下一步操作
    //假设查询商品a,有两种方式,A和B,但是A和B的执行速度不一样,我们希望哪个先返回就用那个的返回值.
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "通过方式A获取商品a";
    });
    CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(2000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "通过方式B获取商品a";
    });
    CompletableFuture futureC = futureA.applyToEither(futureB, product -> "结果:" + product);
    System.out.println(futureC.join());
  }

  public static void acceptEitherTest(){
    //执行两个CompletionStage的结果,那个先执行完了,就是用哪个的返回值进行下一步操作
    //假设查询商品a,有两种方式,A和B,但是A和B的执行速度不一样,我们希望哪个先返回就用那个的返回值.
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "通过方式A获取商品a";
    });
    CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(2000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "通过方式B获取商品a";
    });
    futureA.acceptEither(futureB, product -> {
      System.out.println( "结果:" + product);
    });
  }

  public static void exceptionallyTest(){
    CompletableFuture futureA = CompletableFuture.
        supplyAsync(() -> "执行结果:" + (100 / 0))
        .thenApply(s -> "futureA result:" + s)
        .exceptionally(e -> {
          System.out.println(e.getMessage());
          return "futureA result: 100";
        });
    CompletableFuture futureB = CompletableFuture.
        supplyAsync(() -> "执行结果:" + 50)
        .thenApply(s -> "futureB result:" + s)
        .exceptionally(e -> "futureB result: 100");
    System.out.println(futureA.join());
    System.out.println(futureB.join());
  }

  public static void handleTest(){
    //当CompletableFuture的计算结果完成,或者抛出异常的时候,可以通过handle方法对结果进行处理
    CompletableFuture futureA = CompletableFuture.
    supplyAsync(() -> "执行结果:" + (100 / 0))
    .thenApply(s -> "apply result:" + s)
    .exceptionally(e -> {
      System.out.println("ex:" + e.getMessage());
      return "futureA result: 100";
    })
    .handle((s, e) -> {
      if (e == null) {
        System.out.println(s);
      } else {
        //未执行
        System.out.println(e.getMessage());
      }
      return "handle result:" + (s == null ? "500" : s);
    });
    // 100
    System.out.println(futureA.join());


    CompletableFuture futureB = CompletableFuture.
    supplyAsync(() -> "执行结果:" + (100 / 0))
    .thenApply(s -> "apply result:" + s)
    .handle((s, e) -> {
      if (e == null) {
        //未执行
        System.out.println(s);
      } else {
        System.out.println(e.getMessage());
      }
      return "handle result:" + (s == null ? "500" : s);
    })
    .exceptionally(e -> {
      //未执行
      System.out.println("ex:" + e.getMessage());
      return "futureB result: 100";
    });
    // 500
    System.out.println(futureB.join());

    // handle和whenComplete的区别
    // 1.都是对结果进行处理,handle有返回值,whenComplete没有返回值
    // 2.由于1的存在,使得handle多了一个特性,可在handle里实现exceptionally的功能
  }

  public static void allOfAndAnyOfTest(){
    //allOf:当所有的CompletableFuture都执行完后执行计算
    //anyOf:最快的那个CompletableFuture执行完之后执行计算
    //查询一个商品详情,需要分别去查商品信息,卖家信息,库存信息,订单信息等,这些查询相互独立,在不同的服务上,
    //假设每个查询都需要一到两秒钟,要求总体查询时间小于2秒.
    ExecutorService executorService = Executors.newFixedThreadPool(4);

    long start = System.currentTimeMillis();
    CompletableFuture futureA = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000 + new Random().nextInt(1000));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "商品详情";
    },executorService);

    CompletableFuture futureB = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000 + new Random().nextInt(1000));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "卖家信息";
    },executorService);

    CompletableFuture futureC = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000 + new Random().nextInt(1000));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "库存信息";
    },executorService);

    CompletableFuture futureD = CompletableFuture.supplyAsync(() -> {
      try {
        Thread.sleep(1000 + new Random().nextInt(1000));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      return "订单信息";
    },executorService);

    CompletableFuture allFuture = CompletableFuture.allOf(futureA, futureB, futureC, futureD);
    allFuture.join();

    System.out.println(futureA.join() + futureB.join() + futureC.join() + futureD.join());
    System.out.println("总耗时:" + (System.currentTimeMillis() - start));
  }


  //无返回值
  public static void runAsync() throws Exception {
    System.out.println("------runAsync 无返回值------");
    CompletableFuture future = CompletableFuture.runAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        System.out.println(e);
      }
      System.out.println("run end ...");
    });

    future.get();
    //future.join();
  }

  //有返回值
  public static void supplyAsync() throws Exception {
    System.out.println("------supplyAsync 有返回值------");
    CompletableFuture future = CompletableFuture.supplyAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        System.out.println(e);
      }
      System.out.println("run end ...");
      return System.currentTimeMillis();
    });

    long time = future.get();
    //long time = future.join();
    System.out.println("time = "+time);
  }

  // whenComplete 它可以处理正常的计算结果,或者异常情况。
  public static void whenComplete() throws Exception {
    CompletableFuture future = CompletableFuture.runAsync(() -> {
      try {
        TimeUnit.SECONDS.sleep(1);
      } catch (InterruptedException e) {
        System.out.println(e);
      }
      if(new Random().nextInt()%2>=0) {
        int i = 12/0;
      }
      System.out.println("run end ...");
    });
    // 执行当前任务的线程执行继续执行 whenComplete 的任务。
    future.whenComplete(new BiConsumer() {
      @Override
      public void accept(Void t, Throwable action) {
        System.out.println("执行完成!");
      }

    });
    future.exceptionally(new Function() {
      @Override
      public Void apply(Throwable t) {
        System.out.println("执行失败!"+t.getMessage());
        return null;
      }
    });

    TimeUnit.SECONDS.sleep(2);
  }

  public static void whenComplete2() {
    CompletableFuture futureA = CompletableFuture.
        supplyAsync(() -> "执行结果:" + (100 / 0))
        .thenApply(s -> "apply result:" + s)
        .whenComplete((s, e) -> {
          if (s != null) {
            //未执行
            System.out.println(s);
          }
          if (e == null) {
            //未执行
            System.out.println(s);
          } else {
            System.out.println(e.getMessage());
          }
        })
        .exceptionally(e -> {
          System.out.println("ex"+e.getMessage());
          return "futureA result: 100";
        });
        System.out.println(futureA.join());
  }
}

3.拓展 


import cn.example.improve.DefaultMethod.A;
import cn.example.improve.DefaultMethod.B;
import cn.example.improve.DefaultMethod.C;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.DoubleUnaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Main {
  public static void main(String[] args) {
    // 函数式编程-求子集
    System.out.println("-------函数式编程-求子集-------");
    List> subs = subsets(Arrays.asList(1, 4, 9));
    subs.forEach(System.out::println);

    // 科里化-单位换算
    System.out.println("-------科里化-单位换算-------");
    DoubleUnaryOperator convertCtoF = curriedConverter(9.0/5, 32);
    DoubleUnaryOperator convertUSDtoGBP = curriedConverter(0.6, 0);
    DoubleUnaryOperator convertKmtoMi = curriedConverter(0.6214, 0);

    System.out.println(convertCtoF.applyAsDouble(24));
    System.out.println(convertUSDtoGBP.applyAsDouble(100));
    System.out.println(convertKmtoMi.applyAsDouble(20));

    DoubleUnaryOperator convertFtoC = expandedCurriedConverter(-32, 5.0/9, 0);
    System.out.println(convertFtoC.applyAsDouble(98.6));

    System.out.println(converter(1,2,3));

    // 函数式方法-二叉树
    System.out.println("-------函数式方法-二叉树-------");
    Tree t = new Tree("Mary", 22,
        new Tree("Emily", 20,
            new Tree("Alan", 50, null, null),
            new Tree("Georgie", 23, null, null)
        ),
        new Tree("Tian", 29,
            new Tree("Raoul", 23, null, null),
            null
        )
    );

    System.out.println(lookup("Raoul", -1, t));
    Tree f = fupdate("Jeff", 80, t);
    System.out.println(lookup("Jeff", -1, f));
    Tree u = update("Jim", 40, t);
    System.out.println(lookup("Jim", -1, u));

    // Stream自定义延迟列表
    System.out.println("------Stream自定义延迟列表-------");
    MyList l = new MyLinkedList<>(5, new MyLinkedList<>(10, new Empty<>()));
    System.out.println(l.head());

    LazyList numbers = from(2);
    int two = numbers.head();
    int three = numbers.tail().head();
    int four = numbers.tail().tail().head();
    System.out.println(two + " " + three + " " + four);

    numbers = from(2);
    int prime_two = primes(numbers).head();
    int prime_three = primes(numbers).tail().head();
    int prime_five = primes(numbers).tail().tail().head();
    System.out.println(prime_two + " " + prime_three + " " + prime_five);

    // 模式匹配
    System.out.println("------模式匹配[Java8不支持]-------");
    simplify();

    Expr e = new BinOp("+", new Number(5), new BinOp("*", new Number(3), new Number(4)));
    Integer result = evaluate(e);
    System.out.println(e + " = " + result);

    // 结合器
    System.out.println("------结合器-------");
    System.out.println(repeat(1, (Integer x) -> 2 * x).apply(10));
    System.out.println(repeat(2, (Integer x) -> 2 * x).apply(10));
    System.out.println(repeat(3, (Integer x) -> 2 * x).apply(10));

  }

  // 函数式编程---------------------------------------------------------------------------
  public static List> subsets(List l) {
    if (l.isEmpty()) {
      List> ans = new ArrayList<>();
      ans.add(Collections.emptyList());
      return ans;
    }
    Integer first = l.get(0);
    List rest = l.subList(1,l.size());
    List> subans = subsets(rest);
    List> subans2 = insertAll(first, subans);
    return concat(subans, subans2);
  }

  public static List> insertAll(Integer first, List> lists) {
    List> result = new ArrayList<>();
    for (List l : lists) {
      List copyList = new ArrayList<>();
      copyList.add(first);
      copyList.addAll(l);
      result.add(copyList);
    }
    return result;
  }

  public static List> concat(List> a, List> b) {
    List> r = new ArrayList<>(a);
    r.addAll(b);
    return r;
  }

  // 科里化-------------------------------------------------------------------------------
  public static double converter(double x, double y, double z) {
    return x * y + z;
  }

  public static DoubleUnaryOperator curriedConverter(double y, double z) {
    return (double x) -> x * y + z;
  }

  public static DoubleUnaryOperator expandedCurriedConverter(double w, double y, double z) {
    return (double x) -> (x + w) * y + z;
  }

  // 函数式方法-二叉树---------------------------------------------------------------------
  static class Tree {
    private String key;
    private int val;
    private Tree left, right;

    public Tree(String k, int v, Tree l, Tree r) {
      key = k;
      val = v;
      left = l;
      right = r;
    }
  }
  // 命令式更新值,共享同一份数据结构
  public static Tree update(String k, int newval, Tree t) {
    if (t == null) {
      t = new Tree(k, newval, null, null);
    } else if (k.equals(t.key)) {
      t.val = newval;
    } else if (k.compareTo(t.key) < 0) {
      t.left = update(k, newval, t.left);
    } else {
      t.right = update(k, newval, t.right);
    }
    return t;
  }
  // 函数式更新值,纯函数式
  // 对树结构进行更新时,现存数据结构不会被破坏
  public static Tree fupdate(String k, int newval, Tree t) {
    return (t == null) ? new Tree(k, newval, null, null) :
        k.equals(t.key) ? new Tree(k, newval, t.left, t.right) :
            k.compareTo(t.key) < 0 ? new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) :
                new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right));
  }
  // 递归查询值
  public static int lookup(String k, int defaultval, Tree t) {
    if (t == null) {
      return defaultval;
    }
    if (k.equals(t.key)) {
      return t.val;
    }
    return lookup(k, defaultval, k.compareTo(t.key) < 0 ? t.left : t.right);
  }

  // Stream自定义延迟列表----------------------------------------------------------------
  public interface MyList {
    T head();

    MyList tail();

    default boolean isEmpty() {
      return true;
    }

    MyList filter(Predicate p);
  }

  public static class MyLinkedList implements MyList {
    final T head;
    final MyList tail;

    public MyLinkedList(T head, MyList tail) {
      this.head = head;
      this.tail = tail;
    }
    @Override
    public T head() {
      return head;
    }
    @Override
    public MyList tail() {
      return tail;
    }
    @Override
    public boolean isEmpty() {
      return false;
    }
    @Override
    public MyList filter(Predicate p) {
      return isEmpty() ? this : p.test(head()) ? new MyLinkedList<>(
          head(), tail().filter(p)) : tail().filter(p);
    }
  }

  public static class Empty implements MyList {
    @Override
    public T head() {
      throw new UnsupportedOperationException();
    }
    @Override
    public MyList tail() {
      throw new UnsupportedOperationException();
    }
    @Override
    public MyList filter(Predicate p) {
      return this;
    }
  }

  public static class LazyList implements MyList {
    final T head;
    final Supplier> tail;

    public LazyList(T head, Supplier> tail) {
      this.head = head;
      this.tail = tail;
    }
    @Override
    public T head() {
      return head;
    }
    @Override
    public MyList tail() {
      // 触发延迟列表( LazyList )的节点创建,体现延迟计算的特定
      return tail.get();
    }
    @Override
    public boolean isEmpty() {
      return false;
    }
    @Override
    public MyList filter(Predicate p) {
      return isEmpty() ? this : p.test(head()) ? new LazyList<>(head(), () -> tail().filter(p)) : tail().filter(p);
    }
  }

  public static LazyList from(int n) {
    return new LazyList<>(n, () -> from(n + 1));
  }

  public static MyList primes(MyList numbers) {
    return new LazyList<>(
        numbers.head(), () -> primes(numbers.tail().filter(n -> n % numbers.head() != 0))
    );
  }

  public static  void printAll(MyList numbers) {
    if (numbers.isEmpty()) {
      return;
    }
    System.out.println(numbers.head());
    printAll(numbers.tail());
  }

  // 模式匹配-----------Java8不支持------自动匹配操作符号-----------------------------------
  private static void simplify() {
    TriFunction binopcase =
        (opname, left, right) -> {
          if ("+".equals(opname)) {
            if (left instanceof Number && ((Number) left).val == 0) {
              return right;
            }
            if (right instanceof Number && ((Number) right).val == 0) {
              return left;
            }
          }
          if ("*".equals(opname)) {
            if (left instanceof Number && ((Number) left).val == 1) {
              return right;
            }
            if (right instanceof Number && ((Number) right).val == 1) {
              return left;
            }
          }
          return new BinOp(opname, left, right);
        };
    Function numcase = val -> new Number(val);
    Supplier defaultcase = () -> new Number(0);

    Expr e = new BinOp("+", new Number(5), new Number(0));
    Expr match = patternMatchExpr(e, binopcase, numcase, defaultcase);
    if (match instanceof Number) {
      System.out.println("Number: " + match);
    } else if (match instanceof BinOp) {
      System.out.println("BinOp: " + match);
    }
  }

  private static Integer evaluate(Expr e) {
    Function numcase = val -> val;
    Supplier defaultcase = () -> 0;
    TriFunction binopcase =
        (opname, left, right) -> {
          if ("+".equals(opname)) {
            if (left instanceof Number && right instanceof Number) {
              return ((Number) left).val + ((Number) right).val;
            }
            if (right instanceof Number && left instanceof BinOp) {
              return ((Number) right).val + evaluate((BinOp) left);
            }
            if (left instanceof Number && right instanceof BinOp) {
              return ((Number) left).val + evaluate((BinOp) right);
            }
            if (left instanceof BinOp && right instanceof BinOp) {
              return evaluate((BinOp) left) + evaluate((BinOp) right);
            }
          }
          if ("*".equals(opname)) {
            if (left instanceof Number && right instanceof Number) {
              return ((Number) left).val * ((Number) right).val;
            }
            if (right instanceof Number && left instanceof BinOp) {
              return ((Number) right).val * evaluate((BinOp) left);
            }
            if (left instanceof Number && right instanceof BinOp) {
              return ((Number) left).val * evaluate((BinOp) right);
            }
            if (left instanceof BinOp && right instanceof BinOp) {
              return evaluate((BinOp) left) * evaluate((BinOp) right);
            }
          }
          return defaultcase.get();
        };

    return patternMatchExpr(e, binopcase, numcase, defaultcase);
  }

  public static class Expr {
  }

  public static class Number extends Expr {
    int val;
    public Number(int val) {
      this.val = val;
    }

    @Override
    public String toString() {
      return "" + val;
    }
  }

  public static class BinOp extends Expr {
    String opname;
    Expr left, right;
    public BinOp(String opname, Expr left, Expr right) {
      this.opname = opname;
      this.left = left;
      this.right = right;
    }

    @Override
    public String toString() {
      return "(" + left + " " + opname + " " + right + ")";
    }
  }

  public static  T MyIf(boolean b, Supplier truecase, Supplier falsecase) {
    return b ? truecase.get() : falsecase.get();
  }

  public static interface TriFunction {
    R apply(S s, T t, U u);
  }

  public static  T patternMatchExpr(Expr e,
                                TriFunction binopcase,
                                Function numcase, Supplier defaultcase) {

    if (e instanceof BinOp) {
      return binopcase.apply(((BinOp) e).opname, ((BinOp) e).left, ((BinOp) e).right);
    } else if (e instanceof Number) {
      return numcase.apply(((Number) e).val);
    } else {
      return defaultcase.get();
    }
  }

  // 结合器-------------------------------------------------------------------------------
  // Function
  // R apply(T t);
  public static  Function compose(Function g, Function f) {
    return x -> g.apply(f.apply(x));
  }

  public static  Function repeat(int n, Function f) {
    return n == 0 ? x -> x : compose(f, repeat(n - 1, f));
    // return n == 0 ? x -> x : repeat(n - 1, f).andThen(f);
  }
}
结合器说明
n f g f.apply(x) g.apply(f.apply(x)) 初始值
3 x -> x (Integer x) -> 2 * x 40=40 2*40=80 10
2 x -> x (Integer x) -> 2 * x 20=20 2*20=40
1 x -> x (Integer x) -> 2 * x 10=10 2*10=20
0 x -> x 未执行 10=10  

你可能感兴趣的:(Java,Java8,精华篇)