Java8使用分享

函数式接口

什么是?

  • @FunctionalInterface 注解了的接口类
  • 接口只有一个抽象方法。

java.util.function

  • Function 功能函数 map()

  • Consumer 消费函数 action()

  • Supplier 生产函数 collect()

  • Predicate 谓语判断函数 filter()

  • 包内其他接口说明

    // 类型限制,入参类型固定,返回值固定
    IntFunction IntConsumer
    
    // 数量限制:两个参数在接口类加前缀:Bi Binary 二元,2个输入参数类型
    BiConsumer
    BiFunction
    
    // Operator接口,入参和返回参数一样,一元和二元
    UnaryOperator extends Function
    BinaryOperator extends BiFunction
    

其他函数式接口

  • Comparator 比较器
  • Runnable 线程接口

如何用?

  • 创建接口实现类

  • 创建内部匿名类

  • 使用Lambda表达式

Lambda表达式

什么是?

用来表示一个行为或传递代码,用来创造函数式接口的实例(对象),匿名内部类也可以完成同样的事情。

  • 箭头标识 ->
箭头标识:
() -> 表达式  , s -> { ... }  , (a,b)-> {...} 

方法引用

如果一个Lambda只是直接调用这个方法,那么还是用名称去调用,而不是描述如何调用它。仅仅涉及单一方法的Lambda语法糖。

  • 官网说明methodreferences

  • 双冒号标识 :: 方法引用简化Lambda表达式。

    双冒号标识::,前面是目标引用,后面是方法名称,不需要括号,因为没有实际调用这个方法。
    Integer::sum  list::add String::length HashMap::new
    示例:
    (Apple a) -> a.getWeight()   等效  Apple::getWeight
    (String s) -> System.out.println(s)   等效 System.out::println
    (str, i) -> str.substring(i)  等效  String::substring 
    
Lambda 等效的方法引用
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new

如何用?

// JDK8之前 匿名内部类写法
new Thread(new Runnable(){// 接口名
    @Override
    public void run(){// 方法名
        System.out.println("Thread run()");
    }
}).start();

// JDK8之后 Lambda表达式写法
new Thread(() -> {
    System.out.println("Thread run()");
}).start();// 省略接口名和方法名

//  JDK8之前
list.sort(new Comparator() {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
});
// JDK8之后
list.sort((s1,s2) -> s1.length()-s2.length());
list.sort(comparing(String::length));

// -> 和 双冒号
List list = Arrays.asList("aaaa", "bbbb", "cccc");
list.forEach(s -> System.out.println(s)); // lambda
list.forEach(System.out::println); // method reference

Stream流

什么是?

Stream接口,主要是对集合对象功能的增强。简单理解是某种数据源的视图,数据源不能是Map。

Stream跟函数式接口关系非常紧密,没有函数式接口stream就无法工作。stream只能被“消费”一次。

从有序集合生成流时,会保留原有的顺序。慎用并行BaseStream的parallel()

如何用?

  • 获取Stream

    • 调用Collection.stream()或者Collection.parallelStream() 并行。
    • 调用Arrays.stream(T[] array)方法 。Stream of(T... values) 静态方法。
  • Stream的操作

    • 中间操作只会生成一个标记了该操作的新stream

    • 结束操作会触发实际计算,计算完成后stream就会失效

  • Stream常用api

    • Stream map(Function mapper) 映射器(个数不变,类型可能改变)

    • Stream filter(Predicate predicate) 过滤器

    • Stream sorted(Comparator comparator) 排序器

    • Stream distinct()去重

    • void forEach(Consumer action) 迭代器

    • Optional reduce(BinaryOperator accumulator) 从一组元素生成一个值。

    • R collect(Collector collector) 收集器,生成一个集合或Map对象。

    • boolean anyMatch(Predicate predicate); 一个匹配

    • boolean allMatch(Predicate predicate); 所有匹配

    • boolean noneMatch(Predicate predicate); 没有匹配

    • Optional findAny(); 查找任意一个 并行操作更快

    • Optional findFirst(); 查找第一个

Stream的归集操作 reduce 和 collect

  • reduce操作,max()min() 都是reduce操作

    • 源码分析

      // reduce()最常用的场景就是从一堆值中生成一个值。
      Optional reduce(BinaryOperator accumulator)
      // identity 初始值    
      T reduce(T identity, BinaryOperator accumulator)
      // identity 初始值,accumulator: 累加器,,combiner 并行计算
       U reduce(U identity, BiFunction accumulator, BinaryOperator combiner)
      
      // 求最大值
      Optional max(Comparator comparator);
      Optional min(Comparator comparator);
      
    • 使用示例

      // 1. 找出最长的单词
      Stream stream  = Stream.of("I", "am", "Strean", "reduceMethod");
      String max = stream.reduce((s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2).get();
      String max = stream.reduce("",(s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2); // 比较时慎用初始值
      String max = stream.max((s1,s2) -> s1.length() - s2.length()).get();
      
      // 2. 求出数之和
      Stream stream  = Stream.of("I", "am", "Strean", "reduceMethod");
      Stream stream  = Stream.of(1,2,3,4,5,6,7,8,9,10);
      int sum = stream.reduce(0,(a,b) -> a+b );
      
      // 采用并发流求长度之和
      List list = Arrays.asList("I", "am", "Strean", "reduceMethod");
      Stream stream  = list.stream();// 并发流:parallelStream()
      int lengthSum = stream.reduce(0, (sum, str) -> {
          System.out.println("sum:"+sum+",str:"+str);
          return sum+str.length();},(a,b) -> {
          System.out.println("a:"+a+",b:"+b); // 标准流式没有
          return a+b;
      });
      
  • collect操作,终极武器

    一般流先接map映射器方法,再来归集操作

    • Collector是为Stream.collect()方法量身打造的接口,而Collectors是产生Collector接口的工具类,包含了CollectorImpl实现类。

    • 源码分析

      // Stream 的collect方法
       R collect(Collector collector);
      
      // supplier目前容器生产方式,accumulator累加器,元素如何加到容器中
       R collect(Supplier supplier,BiConsumer accumulator, BiConsumer combiner);
      
    • 使用示例

      List list = Arrays.asList("I", "am", "Strean", "reduceMethod");
      // 基础用法:
      List newList = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toList());
      Set newSet = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toSet());
      
      // 将list集合转成Map
      Map map= list.stream().collect(Collectors.toMap(t->t, t -> t.length()));
      Map map= list.stream().collect(Collectors.toMap(Function.identity(),String::legth));
      

Optional类

什么是?

java.util.Optional 是一个容器类,代表一个值存在或不存在,不能被序列化。

如何用?

  • 创建Optional对象

    Optional optStu = Optional.empty();// 1.空对象
    Optional optStu = Optional.of(student); // 2.非空值
    Optional optStu = Optional.ofNullable(student); // 3.允许null值
    
  • 常用API,支持链式操作。

    public Optional map(Function mapper)
    public T orElse(T other)
    public T orElseGet(Supplier other)
    public void ifPresent(Consumer consumer)
    public Optional filter(Predicate predicate)
    public Optional flatMap(Function> mapper)
    public  T orElseThrow(Supplier exceptionSupplier) throws X
    

时间日期类

Java8之前,Date类都是可变类。而Java8的Date和Time API提供 线程安全的不可变类且线程安全的。

  • LocalDateLocalTime , LocalDateTimeYearDurationPeriod

    // 年月日
    System.out.println("LocalDate.now():" + LocalDate.now()); // 2019-07-09
    // 时分秒
    System.out.println("LocalTime.now():" + LocalTime.now()); // 09:38:26.014
    // 完整日期
    System.out.println("LocalDateTime.now():" + LocalDateTime.now()); // 2019-07-09T09:38:26.015
    // Duration是用来计算两个给定的日期之间包含多少秒,多少毫秒
    LocalDate localDate = LocalDate.now();
    // Period:两个给定的日期之间包含多少天,,多少月或者多少年。记住整数,年,月,日
    Period period = Period.between(localDate, localDate.plus(30, ChronoUnit.DAYS));
    System.out.println(period.getMonths());
    
  • DateTimeFormatter

    LocalDateTime localDateTime = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    System.out.println("localDateTime格式化:" + dtf.format(localDateTime)); // 2019-07-09T10:54:20.661
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
    System.out.println("formatter格式化:" + formatter.format(localDateTime)); // 2019-07-09 10:54:20
    //  String timeStr = "2019-07-09 10:48:05"; 如果使用这个parse会出现错误
    formatter.parse(timeStr).get(ChronoField.DAY_OF_MONTH) // 9
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    String timeStr = "2019-07-09T11:02:01.840";
    LocalDateTime time = LocalDateTime.parse(timeStr,dtf);
    
    /**
     * DateTimeFormatter won't parse dates with custom format "yyyyMMddHHmmssSSS"
     * https://bugs.openjdk.java.net/browse/JDK-8031085
     */
    

Java8与重构

方法参数传递由值传递,转变为类(对象)传递,匿名类,Lambda,方法引用。这是:值参数化到行为参数化的转变。

为了因对不断变化的需求,行为参数化。抽象化,使代码更加灵活,后期维护代码更加方便!

总结

学习三部曲:什么是? what 如何用? how 为什么? why

函数式编程又称λ演算(lambda calculus),匿名函数。在编程语言中的函数通常指的是方法。从接口式编程,到函数式编程!

Java8 提倡函数式编程:

  1. 简少代码量:减少for...each 语句,实现类,匿名内部类。
  2. 增加代码清晰度:方法内直接写明操作行为,更加好维护
  3. 提高计算效率:让并行计算更加简单

你可能感兴趣的:(Java8使用分享)