美文网首页
Java8使用分享

Java8使用分享

作者: cjp1016 | 来源:发表于2019-07-09 17:19 被阅读0次

    函数式接口

    什么是?

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

    java.util.function

    • Function<T, R> 功能函数 map()

    • Consumer<T> 消费函数 action()

    • Supplier<T> 生产函数 collect()

    • Predicate<T> 谓语判断函数 filter()

    • 包内其他接口说明

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

    其他函数式接口

    • Comparator<T> 比较器
    • 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<String>() {
        @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<String> 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<T> of(T... values) 静态方法。
    • Stream的操作

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

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

    • Stream常用api

      • Stream map(Function<? super T,? extends R> mapper) 映射器(个数不变,类型可能改变)

      • Stream filter(Predicate<? super T> predicate) 过滤器

      • Stream sorted(Comparator<? super T> comparator) 排序器

      • Stream distinct()去重

      • void forEach(Consumer<? super T> action) 迭代器

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

      • <R, A> R collect(Collector<? super T, A, R> collector) 收集器,生成一个集合或Map对象。

      • boolean anyMatch(Predicate<? super T> predicate); 一个匹配

      • boolean allMatch(Predicate<? super T> predicate); 所有匹配

      • boolean noneMatch(Predicate<? super T> predicate); 没有匹配

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

      • Optional<T> findFirst(); 查找第一个

    Stream的归集操作 reduce 和 collect

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

      • 源码分析

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

        // 1. 找出最长的单词
        Stream<String> 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<String> stream  = Stream.of("I", "am", "Strean", "reduceMethod");
        Stream<Integer> stream  = Stream.of(1,2,3,4,5,6,7,8,9,10);
        int sum = stream.reduce(0,(a,b) -> a+b );
        
        // 采用并发流求长度之和
        List<String> list = Arrays.asList("I", "am", "Strean", "reduceMethod");
        Stream<String> 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, A> R collect(Collector<? super T, A, R> collector);
        
        // supplier目前容器生产方式,accumulator累加器,元素如何加到容器中
        <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
        
      • 使用示例

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

    Optional类

    什么是?

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

    如何用?

    • 创建Optional对象

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

      public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
      public T orElse(T other)
      public T orElseGet(Supplier<? extends T> other)
      public void ifPresent(Consumer<? super T> consumer)
      public Optional<T> filter(Predicate<? super T> predicate)
      public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
      public <X extends Throwable> T orElseThrow(Supplier<? extends X> 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使用分享

          本文链接:https://www.haomeiwen.com/subject/rlrfkctx.html