JavaSE | Lambda| Optional| Stream API

Posted kris12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE | Lambda| Optional| Stream API相关的知识,希望对你有一定的参考价值。

 JDK1.8新特性

* JDK1.8或Java8的新特性:
 * 1、接口:默认方法、 静态方法
 * 2、Lambda表达式和StreamAPI
 * 3、Optional类
 * 4、新的日期时间API
 * 
 * Lambda表达式:为了简化代码,使得Java支持
 * StreamAPI:为了支持内存中的数据的筛选、管理等
 * Optional类:为了避免,简化空指针的处理
 * 新的日期时间API:为了解决原来老版的日期时间(1)对象的可变性(2)闰秒的考虑(3)本地化考虑
 * 
 * 一、Lambda表达式
 * Lambda表达式是实现SAM接口的语法糖,使得Java支持函数式编程。
 * 
 * 原来Java要给Runnable类型的形参,一个对象:
 * Thread(Runnable target),给target赋值一个对象,一个匿名内部类的对象
 * new Runnable(){

            @Override
            public void run() {
                System.out.println("hello");
            }
            
        }
 *
 * 现在使用Lambda表达式:
 *  Thread(Runnable target),给target赋值的是一段代码:
 * () -> System.out.println("hello")
 *
 * 左边():其实是方法的形参列表
 * 右边:System.out.println("hello")是方法体
 * 
 * () -> System.out.println("hello")相当于是一个函数。
 * 
 * 
 * SAM接口:函数式接口,Single Abstract Method,这个接口只有一个唯一的抽象方法。
 * 例如:Runnable接口是一个函数式接口,它只有一个抽象方法:public void run();
 * 
 * 换句话说:目前Java的Lambda表达式,只适合给SAM接口赋值。

 

 

* Lambda表达式是给SAM接口赋值的,因此SAM接口的抽象方法的方法签名就很重要了。
 * 重点关注抽象方法的:形参列表,返回值类型
 * 
 * Lambda表达式的语法:
 * (形参列表) -> {Lambda体}

 * 说明:
 * (1)Lambda表达式的(形参列表)就是你给赋值的SAM接口的抽象方法的形参列表
 * (2)->是称为Lambda操作符,中间不要加空格
 * (3){Lambda体}就是你给赋值的SAM接口的抽象方法的方法体

 * 方法的形式:
 * 1、无参无返回值
 * 2、有参无返回值
 * 3、无参有返回值
 * 4、有参有返回值
 * 
 * 提示:
 * (1)如果我们的(形参列表),只有一个形参,并且类型是已知的或可以推断的,那么可以省略(形参的类型),只要写形参名
 * (2)如果{Lambda体}只有一个语句,那么可以省略{}和这句语句的;,并且如果这一句是return语句,这个return也可以省略
 * (3)如果我们的(形参列表),无参的,或者是参数有多个的,那么()绝对不能省略
 * (4)如果抽象方法有返回值,并且{}没有省略的话,那么return也不能省略
 */

 

函数式接口:SAM接口
 * Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。
 * 
 * 原来JDK1.8之前就已经存在以下接口,符号这样的特征:
 * (1)java.lang.Runnable:public void run();
 * (2)java.lang.Comparable<T>:int compareTo(T t)
 * (3)java.util.Comparator<T>:int compare(T t1, T t2)
 * (4)java.io.FileFilter:public boolean accept(File pathname)
 * (5)java.lang.reflect.InvocationHandler:public Object invoke(Object proxy, Method method, Object[] args)
 * (6)java.lang.Iterable<T>:  Iterator iterator()
 * ....
 * 
 * 函数式接口最好用一个注解标记一下:@FunctionalInterface
 * 
 * JDK1.8之前的那些满足SAM特征的接口是否都加了呢?
 * (1)java.lang.Runnable
 * (3)java.util.Comparator<T>
 * (4)java.io.FileFilter
 * 如果没加@FunctionalInterface,表示将来可能扩展其他的抽象方法,变成非SAM接口。
 * 结论:最好只对加了@FunctionalInterface的接口使用Lambda表达式。
 * 
 * JDK1.8之后又增加了很多新的函数式接口。

 

* JDK1.8增加的函数式接口:java.util.function
 * 它设计的这些个接口,基本能保证大多数接口的需求,不需要重新设计新的接口。
 * 分为四大类:
 * 1、消费型接口:它的抽象方法的特征   有参无返回值
 * 2、供给型接口:它的抽象方法的特征   无参有返回值
 * 3、判断型接口:它的抽象方法的特征   有参有返回值,并且返回值类型是boolean
 * 4、功能型接口:它的抽象方法的特征   有参有返回值
 * 
 * 一、消费型接口:有参无返回值
 * 1、Consumer<T>  void accept(T t)
 * 2、BiConsumer<T,U> void accept(T t,U u)
 * 3、DoubleConsumer  void accept(double value) 
 * 4、IntConsumer  void accept(int value) 
 * 5、LongConsumer  void accept(long value) 
 * 6、ObjDoubleConsumer<T> void accept(T t, double value)
 * 6、ObjIntConsumer<T> void accept(T t, int value)
 * 6、ObjLongConsumer<T> void accept(T t, long value)


* 二、供给型接口:无参有返回值
 * 1、Supplier<T>   T get() 
 * 2、BooleanSupplier boolean  getAsBoolean()  
 * 3、DoubleSupplier double getAsDouble()
 * 4、IntSupplier int getAsInt()
 * 5、LongSupplier long getAsLong()

* 三、判断型接口:有参,但是返回值类型是boolean结果
 * 1、Predicate<T>  boolean test(T t)
 * 2、BiPredicate<T,U> boolean test(T t,U u)
 * 3、DoublePredicate  boolean(double value)
 * 4、IntPredicate  boolean(int value)
 * 5、LongPredicate  boolean(long value)

* 四、功能型接口:有参有返回值
 * 1、Function<T,R>  R apply(T t) 
 * 2、UnaryOperator<T> T apply(T t)  与上一个的区别就是形参的类型与返回值的类型是一样

 * 3、DoubleFunction<R> R apply(double value)
 * 4、IntFunction<R> R apply(int value)
 * 5、LongFunction<R> R apply(long value)
 
 * 6、ToDoubleFunction<T> double apply(T t)
 * 7、ToIntFunction<T>  int apply(T t)
 * 8、ToLongFunction<T>  long apply(T t)

 * 9、DoubleToIntFunction int int applyAsInt(double value)  
 * 10、DoubleToLongFunction......
 * 11、IntToDoubleFunction......
 * 12、IntToLongFunction......
 * 13、LongToDoubleFunction......
 * 14、LongToIntFunction ......
 *  
 * 15、DoubleUnaryOperator  double applyAsDouble(double value)
 * 16IntUnaryOperator...
 * 17、LongUnaryOperator...
 * 
 * 19、BiFunction<T,U,R>   R apply(T t, U u) 
 * 20、BinaryOperator<T>  T apply(T t1, T t2)
 * 21、ToDoubleBiFunction<T,U> double apply(T t, U u) 
 * ....


 * 方法引用和构造引用:
 * 当Lambda表达式出现更特殊的情况时,我们可以对Lambda表达式进行再次简化。
 * (1)当{Lambda体}的实现是通过调用一个类或一个对象的现有的方法来完成功能时。
 * (2)你这个Lambda表达式所赋值的SAM接口的抽象方法的形参列表与返回值类型与实现{Lambda体}所调用的方法的形参列表与返回值类型对应。
 * 
 * 方法引用的语法:
 * (1)对象名::实例方法名
 * (2)类名::静态方法名
 * (3)类名::实例方法名
 * 构造引用:
 * (4)构造器名::new
 * (5)数组类型::new
 */

 Optional类

* 设计Optional类的初衷:因为Java的对象可能为null值,当用null值去调用方法,属性等时,会报NullPointerExecption空指针异常。
 * 程序中为了避免这个空指针异常,会需要加入大量的非空判断,这样会导致程序中有很多重复的非空判断。
 * 
 * Optional是一个容器。
 * 数组和集合是用来装一堆(大量)对象的容器,而Optional是用来包装一个对象的容器。
 * 
 * java.util.Optional<T>,这个T就是所包装的对象的类型。
 * 1、如何包装对象?
 * (1)static <T> Optional<T> empty()  :包装一个空对象
 * (2)static <T> Optional<T> of(T value)  :只能用来包装“非空”对象
 * (3)static <T> Optional<T> ofNullable(T value)  :包装一个对象,这个对象可以是null,也可以非空
 * 
 * 2、如何拿出这个被包装的对象
 * (1)T get()  
 *     可以取出Optional容器中的对象,但是如果容器中的对象是null,那么会报java.util.NoSuchElementException: No value present
 * (2)T orElse(T other)  
 * 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就用other来代替。
 * (3)T orElseGet(Supplier<? extends T> other)  
 * 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就用由Supplier这个供给型接口提供的对象来代替
 * (4) T orElseThrow(Supplier<X> exceptionSupplier) 
 * 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就抛出你指定的异常对象
 * 
 * 3、对所包装的对象,进行判断或处理
 * (1)Optional<T> filter(Predicate<? super T> predicate)  :
 * 如果Optional容器中的对象满足你指定的条件,就保留,否则就清空。
 * (2)<U> Optional<U> map(Function<? super T,? extends U> mapper)  
 * 如果Optional容器中的对象非空,可以对该对象进行处理,如何处理,由Function接口的lambda体来决定。

 

 StreamAPI

* 原来的数据都在数据库中,现在很多数据都在内存中,例如:在集合、数组等容器中。
 * 我们要对内存中的数据进行筛选、查找等各种操作,那么我们就可以使用StreamAPI,
 * 像我们用SQL语句对象数据库一样操作。
 * 
 * Stream是一个数据的渠道,专门对数据进行加工处理的渠道,不是数据本身。
 * Stream的特点:
 * (1)不负责存储数据,存储数据仍然是集合、数组等容器。
 * (2)Stream对数据的加工,不会影响数据源(集合、数组等容器中的数据),而是产生一个新的副本。
 * 每一次对Stream的操作都会产生一个新的Stream对象。
 * (3)Stream的加工操作是一个延迟操作,只有在最后拿结果时,才一口气执行完,
 * 创建Stream->(1)加工1(2)加工2....->取结果
 * 
 * Stream的操作分为三步:
 * (1)创建一个Stream
 * (2)中间操作:可以很多步
 * (3)终结操作:拿结果
 * 一旦结束,这个Stream结束了,如果要重新加工,要重新创建。
 * 
一、如何创建Stream
 * 1、通过集合来创建Stream
 * Collection系列的集合.stream()
 * 
 * 2、通过数组来创建Stream
 * Arrays.stream(数组)
 * 
 * 3、Stream.of(...)
 * 
 * 4、无限流
 * 
 * static <T> Stream<T> generate(Supplier<T> s)  
 * static <T> Stream<T> iterate(T seed, UnaryOperator<T> f) 
 */
    @Test
    public void test1(){
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("world");
        
        String[] arr = {"hello","java","world"};
        
        Stream<String> stream = list.stream();
        Stream<String> stream2 = Arrays.stream(arr);
        
        Stream.of("Hi","kk");
    }
    
    @Test
    public void test2(){
        Stream<Integer> ite = Stream.iterate(1, t -> t + 2); //T seed  :UnaryOperator<T> 是一个SAM接口,是一个功能型接口
        //抽象方法:T apply(T t)
        ite.forEach((t) -> System.out.println(t));
        //ite.forEach(System.out::println);
    
    }

中间操作----每次中间操作都会返回一个新的stream,要重新接收

  @Test
    public void test1(){
        //(1)filter(Predicate p):按照p指定的条件进行过滤
        Stream<Integer> stre = Stream.of(1, 2, 3, 4, 5, 6);
        Stream<Integer> fil = stre.filter((t) -> t%2 == 0);
        fil.forEach(System.out::println);
        
        //(2)distinct():去重
        Stream.of(1,3,6,2,6,6,8,9)
        .distinct() //去除重复的元素
        .forEach(System.out::println);
        
        //(3)limit(long maxSize):取出前maxSize个
        Stream.of(1,3,6,2,6,6,8,9)
        .limit(3) //取前3个
        .forEach(System.out::println);
        
         //(4)skip(long n):跳过前n个
        Stream.of(1,3,6,2,6,6,8,9)
        .skip(4) //把前4个跳过去
        .forEach(System.out::println);
        

        //(5)peek(Consumer action) 
        long count = Stream.of(1,3,6,2,6,6,8,9)
        .peek((t) -> System.out.println(t))
        //.forEach(System.out::println); //不加.count输出是每个元素都重复了2遍;
        .count();
        System.out.println("count=" + count); //8
        
        //(6)sorted()   要求元素实现java.lang.Comparable
        Stream.of(7,3,6,2,4,6,8,9)  //创建Stream
        .sorted()   //从小到大排序
        .forEach(t -> System.out.println(t));  //终结操作
        
        //(7)sorted(Comparator com)可以指定定制比较器
        ArrayList<Employee> list = new ArrayList<>();
        list.add(new Employee(2, "张三", 100000));
        list.add(new Employee(1, "李四", 8000));
        list.add(new Employee(3, "王五", 9000));
        //list.stream().sorted((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
        list.stream().sorted((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()))
        .forEach(System.out::println);
        
        //(8)map(Function f):给stream中的每一个数据进行xx操作,结果仍然放回stream中,最终生成一个新的流
        list.stream()  //设置值!!!!
        .map(t -> {t.setSalary(t.getSalary() + 100); return t;})  //中间操作   Function<T,R>:  R apply(T t)
        .forEach(System.out::println);
        
        //(9)mapToDouble(ToDoubleFunction f)
        OptionalDouble reduce = list.stream()
        .mapToDouble((t) -> t.getSalary())
        .reduce((t1, t2) -> t1 + t2);
        System.out.println(reduce.getAsDouble()); //117300.0
        
        /*(10)flatMap(Function<? super T,? extends Stream<? extends R>> mapper) 
         * Function<T,R>的抽象方法   R apply(T t),这的R是一个Stream
         * 对stream中的每一个数据进行某个操作,会得到一个stream,然后把这些个stream再合成一个大的stream
         */
        Stream.of("hello","java","wolrd")
        .flatMap(t -> Stream.of(t.split("|")))
        .forEach(System.out::println);
        
    }

 

 

* Stream:
 * (1)创建:必须有
 * (2)中间操作:0~n步
 * (3)终结操作:必须有
 * 
 * 三、Stream的终结操作
 * 1、void forEach(Consumer c):遍历stream中的数据
 * 2、long count():统计流中的数据的个数
 * 3、boolean allMatch(Predicate p):判断流中的数据是否都满足p的条件
 *   boolean anyMatch(Predicate p):判断流中的数据是否至少有一个满足p的条件
 *   boolean noneMatch(Predicate  p):判断流中的数据是否都不满足p的条件
 * 4、Optional<T> findFirst() :返回流中的第一个
 *   Optional<T> findAny() :返回流中的任意一个,如果流是固定的,那么相当于findFirst()
 *   
 * 5、Optional<T> max(Comparator c)  
 *   Optional<T> min(Comparator c)  
 *   
 * 6、U reduce(BinaryOperator b)  :把流中的数据,返回结合,得到一个值
 * 7、R collect(Collector c):把流中的数据收集起来放到一个容器中
 * 
 * 工具类:Collectors 
 * 
 * 
 * 区别:
 * Collection和Collector
 * Collections(Collection集合的工具类)和Collectors

 

以上是关于JavaSE | Lambda| Optional| Stream API的主要内容,如果未能解决你的问题,请参考以下文章

java lambda - 如何遍历可选列表/可选流

Java8 新特性 -- Lambda表达式:函数式接口方法的默认实现和静态方法方法引用注解类型推测Optional类Stream类调用JavaScriptBase64

深入理解 lambda表达式 与 Optional Null 源码解析(Java11 三)

JavaSE——Lambda表达式

JavaSE-22.1.6Lambda表达式的注意事项

JavaSE Lambda表达式(JDK1.8新特性)