Lambda表达式与StreamAPI

Lambda表达式与StreamAPI

这两个的目的都是想要使得Java能够实现函数式编程

Lambada表达式主要针对接口,函数式接口进行的优化,简化代码

StreamAPI主要是针对集合的处理操作进行的优化,简化代码

Lambda表达式

是一个匿名的函数,为了把方法体的实现代码当做数据一样进行传递

JDK1.8之前,用匿名内部类完成

//目的就是传递run()方法的实现代码
Runnable r = new Runnable(){
    public void run(){
        ......
    }
}Runnable r = new Runnable(){
    public void run(){
        ......
    }
}


new Thread(new Runnable(){
    public void run(){
        ......
    }
}).start();

使用Lambda表达式

  Runnable r = () -> {....};

  new Thread(() -> {....}).start();

语法

  格式: (形参列表) ->  {Lambda体}

四种形式

  (1)无参无返回值

    格式:() -> {Lambda体}

     说明:

      (1)无参,()

      (2)如果Lambda体是多句语句,那么{}是不可以省略

      (3)如果Lambda体是1句语句,那么{}如果不省略,那么语句后面要加; ,即{;}

      (4)如果Lambda体是1句语句,那么{;}可以省略

    代表

      Runnable r = ()  -> {System.out.println("hello");};

      Runnable r = ()  -> System.out.println("hello");

  (2)有参无返回值

    格式:(形参列表) -> {Lambda体}

    说明:

       (1)如果Lambda体是多句语句,那么{}是不可以省略

       (2)如果Lambda体是1句语句,那么{}如果不省略,那么语句后面要加; ,即{;}

       (3)如果Lambda体是1句语句,那么{;}可以省略

      (4)如果参数列表的参数类型是可以确定的,那么类型可以省略,如果形参个数是多个,那么()不可以省略

       (5)如果参数列表的个数只有1个,类型可以确定,()可以省略,形参名不可以省略,但是形参名可以和接口的抽象方法的形参名不一样

    代表

      消费型

      Consumer<T>   void  accept(T t)

      java.lang.Iterable<T>

        forEach(Consumer<T>)

        集合对象.forEach(t  -> System.out.println(t));

  (3)无参有返回值

    格式:() -> {Lambda体}  Lambda体中,肯定有return语句

    说明:

       (1)无参,()不可以省略

       (2)如果Lambda体是多句语句,那么{}是不可以省略,,每一个语句后面要;,并且必须有return 返回值;的语句

       (3)如果Lambda体是1句语句,那么{}如果不省略,那么一定是{return 返回值;}的语句

       (4)如果Lambda体是1句语句,那么{;}可以省略,而且return要省略

    代表

      供给型

      Supplier<T>   T get()

      java.util.Optional<T>   orElseGet(Supplier)

        Optional<String> opt = Optional.ofNullable(address);

        String address = opt.orElseGet(() -> new String("北京"));

  (4)有参有返回值

    格式:(形参列表) -> {Lambda体}

    说明:

       (1)如果Lambda体是多句语句,那么{}是不可以省略,并且必须有return 返回值;的语句

       (2)如果Lambda体是1句语句,那么{}如果不省略,那么一定是{return 返回值;}的语句

       (3)如果Lambda体是1句语句,那么{;}可以省略,而且return也可以省略

       (4)参数列表,如果类型可以确定,那么类型可以省略,()不可以省略

       (5)参数列表,如果类型可以确定,并且参数个数只有一个,那么类型和()都可以省略

    代表

      java.util.Comparator<T>: int compare(T t1, T t2)

        Comparator<Student> com = (t1,t2) -> t1.getId() - t2.getId();

      Predicate<T>  : boolean  test(T t)

        Predicate<Employee>  p = t -> t.getSalary() > 10000;

      Function<T,R> :  R apply(T t)

        如果Employee对象的薪资低于10000,涨薪10%,并且返回新工作

Function<Employee, Double>  f = e -> {
        if(e.getSalary()<10000){
            e.setSalary(e.getSalary()*1.1;
        }
        return e.getSalary();    
}

使用的形式

  1、给函数式接口的变量赋值:多态引用  Comparator<Student> com = (t1,t2) -> t1.getId() - t2.getId();

  2、给函数式接口的形参赋值:多态参数  new TreeSet( (t1,t2) -> t1.getId() - t2.getId());

函数式接口

  函数式接口也是接口,是个特殊的接口,SAM型接口  SAM:Single  Abstract  Method  使用注解声明@FunctionInterface

  在这个类接口中只有一个抽象方法,但是可以有静态方法和默认方法以及Object中的方法

  只有函数式接口的变量或形参才能用Lambda表达式赋值

  四个核心的函数式

    1、消费型

      Consumer<T>   void  accept(T t)

      延伸版:BiConsumer<T,U>: void  accept(T t,U u)

    2、供给型

      Supplier<T>   T get()

      DoubleSupplier   Double get()

    3、功能型

      Function<T,R>  R  apply(T t)

      延伸版

        BiFunction<T,U,R>  R apply(T t,U u)

        BinaryOperator<T>  T apply(T t2, T t2)

    4、断定型

      Predicate<T>  : boolean  test(T t)

      延伸版:BiPredicate<T,U>  boolean test(T t ,U u)

再简化版

  方法引用

    条件

      (1)Lambda体通过调用一个现成的方法来完成

      (2)Lambda的形参列表刚好是用于这个方法的实参

    形式有三种

      (1)实例对象::实例方法名

        集合对象.forEach(t -> System.out.println(t));

        集合对象.forEach(System.out::println)

      (2)类名::静态方法名

        Supplier<Double>  s = () -> Math.random();  Supplier<Double>  s = Math::random;

        BiFunction<Integer,Integer,Integer> b = (a,b) -> Math.max(a,b);  BiFunction<Integer,Integer,Integer> b = Math::max;

      (3)类名::实例方法名

        Comparator<String>  c = (t1,t2)  ->  t1.compareTo(t2);

          Lambda表达式的形参的第一个作为调用方法的对象,第二个,作为这个实例方法的实参

          Comparator<String>  c = String::compareTo;

  构造器引用

    条件

      (1)Lambda体通过调用一个构造器完成,返回这个新建的对象

      (2)Lambda的形参列表刚好是用于这个构造器的实参

    示例

      Fuction<String ,Employee>  f = name -> new Employee(name);

      Fuction<String ,Employee>  f = Employee::new;

  数组构造的引用

    条件

      (1)Lambda体通过创建一个数组对象完成,返回这个新建的数组对象

      (2)Lambda的形参列表刚好是用于这个数组对象的创建的长度

    示例

      Fuction<Integer ,Employee[]>  f = len -> new Employee[len];

      Fuction<Integer ,Employee[]>  f = Employee[]::new;

StreamAPI

作用

  Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
  Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

特点

  1、Stream不负责存储数据

  2、Stream的操作不影响数据源,每次操作返回一个新的Stream

  3、Stream的中间操作是一个“懒惰求值”,一直要到终结操作,才会一口气完成

步骤

  1、创建Stream

    四种方式

      1、Collection集合对象.stream()  

        ArrayList<String> list = new ArrayList<>();
        //创建stream
        Stream<String> stream = list.stream();

      2、Arrays.stream(数组对象)

        String[] arr = {"hello","world","java","lambda","stream"};
        Stream<String> stream = Arrays.stream(arr);

      3、Stream.of(T...)

        Stream<String> stream = Stream.of("hello","world","java","lambda","stream");

      4、无限流

        Stream.iterator(..)

//示例
    @Test
    public void test4(){
        //Interface UnaryOperator<T>是特殊的Function<T,T>: T apply(T t)
        Stream<Integer> stream = Stream.iterate(1, x -> x+2);
        
        //中间操作
        stream = stream.limit(10);//取前面的10个
        
        stream.forEach(System.out::println);
    }

        Stream.generate(xx)

//示例
    @Test
    public void test5(){
        //Supplier<T> :T  apply()
//        Stream<Double> generate = Stream.generate(() -> Math.random());
        Stream<Double> stream = Stream.generate(Math::random);
        
//        stream = stream.limit(5);
//        stream.forEach(System.out::println);
        
        stream.limit(5).forEach(System.out::println);
    }

  2、中间操作

    (1)筛选与切片 

      A:过滤:filter
      B:去重:distinct
      C:取前面的几个:limit
      D:跳过前面几个:skip
      * 要取中间几个:skip + limit

    (2)映射

      A:map(Function):对Stream中的每一个元素执行某个操作,返回值组成一个新的Stream
      B:返回值是特殊类型
      * mapToInt()
      * mapToLong()
      * mapToDouble()
      C:flatMap(Function):对stream的每一个元素执行某个操作,返回值是一个stream,这些个stream再合成一个大的stream

    (3)排序

      A:sorted():自然排序
      B:sorted(Comparator):定制排序

  3、终结操作

    (1)遍历

      forEach(Consumer)

    (2)匹配与查找  

      A:allMatch(Predicate p):每一个元素是否都满足某个条件
      B:anyMatch(Predicate p):是否有一个元素满足条件
      C:noneMatch(Predicate p):是否所有元素都不满足
      *
      D:findFirst():返回第一个
      E:findAny():如果是并行流,返回的结果是任意一个,如果是一个稳定的流,返回的结果是和findFirst()一样
      *
      F:count():统计
      G:max(Comparator)
      H:min(Comparator)

    (3)规约

      A:Optional<T> reduce(BinaryOperator<T> accumulator)
      BinaryOperator<T>是一个BiFunction<T,T,T>接口: T apply(T t1, T t2)
      B:T reduce(T identity,BinaryOperator<T> accumulator)

    (4)收集

      <R,A> R collect(Collector<? super T,A,R> collector)

      Collector 接口中方法的实现决定了如何对流执行收集的操作
      Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,例如、List,Set,Map

posted @ 2023-03-06 20:39  LuckySnail  阅读(33)  评论(0)    收藏  举报