zoukankan      html  css  js  c++  java
  • JavaSE | Lambda| Optional| Stream API

     JDK1.8新特性

       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终结操作

    * 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
      @Test
        public void test1(){
            Random random = new Random();
            long count = Stream.generate(() -> random.nextInt(100))
            .limit(10)
            .peek(System.out::println)
            .filter(t -> t%2 == 0)
            .count();
            System.out.println("偶数的个数:" + count);
        }
        
        @Test
        public void test2(){
            boolean flag = Stream.of(1, 2, 3, 4)
            .allMatch(num -> num < 4);
            System.out.println(flag); //false
        }
        
        @Test
        public void test3(){
            ArrayList<Employee> list = new ArrayList<>();
            list.add(new Employee(2, "张三", 100000));
            list.add(new Employee(1, "李四", 8000));
            list.add(new Employee(3, "王五", 9000));
            Optional<Employee> max = list.stream()
            .max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
            System.out.println(max.get().getSalary());
            
            Optional<Integer> sum = Stream.of(1,2,3,4)
            .reduce((t1, t2) -> t1+t2);
            System.out.println(sum);
        }
        
        @Test
        public void test4(){
            ArrayList<Employee> list = new ArrayList<>();
            list.add(new Employee(2, "张三", 100000));
            list.add(new Employee(1, "李四", 8000));
            list.add(new Employee(3, "王五", 9000));
            list.add(new Employee(4, "小路", 7000));
            list.add(new Employee(5, "小周", 8800));
            
            //找出所有薪资低于10000的员工,放到一个集合中
            List<Employee> collect = list.stream()
            .filter(emp -> emp.getSalary() < 10000)
            .collect(Collectors.toList());
            for (Employee employee : collect) {
                System.out.println(employee);
            }
        }
  • 相关阅读:
    AngularJs学习笔记Understanding the Controller Component
    AngularJs学习笔记Dependency Injection(DI,依赖注入)
    AngularJs学习笔记Forms
    AngularJs学习笔记Modules
    AngularJs学习笔记IE Compatibility 兼容老版本IE
    Oracle trigger Demo
    Debugging tips in VS
    Adding a Strong Name to an existing DLL that you don't have the source to
    Webservice
    Tips to import DB dump of a big size
  • 原文地址:https://www.cnblogs.com/shengyang17/p/10124525.html
Copyright © 2011-2022 走看看