目录

Lambda表达式与StreamAPI


Lambda 表达式与 StreamAPI

1 JDK1.8

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

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

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

5 Lambda 表达式

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

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

  • Runnable r = new Runnable(){ public void run(){ …… } }

    - 目的就是传递run()方法的实现代码
    
  • new Thread(new Runnable(){ public void run(){ …… } }).start();

5.3 使用 Lambda 表达式

  • Runnable r = () -> {….};
  • new Thread(() -> {….}).start();

5.4 语法

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

5.5 四种形式

  • (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 void accept(T t)

      • java.lang.Iterable

        • forEach(Consumer)
        • 集合对象.forEach(t -> System.out.println(t));
  • (3)无参有返回值

    • 格式

      • () -> {Lambda 体}

        • Lambda 体中,肯定有 return 语句
    • 说明:

      • (1)无参,()不可以省略
      • (2)如果 Lambda 体是多句语句,那么{}是不可以省略,,每一个语句后面要;,并且必须有 return 返回值;的语句
      • (3)如果 Lambda 体是 1 句语句,那么{}如果不省略,那么一定是{return 返回值;}的语句
      • (4)如果 Lambda 体是 1 句语句,那么{;}可以省略,而且 return 要省略
    • 代表

      • 供给型

      • Supplier T get()

      • java.util.Optional orElseGet(Supplier)

        • Optional 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();
    

    }

5.6 使用的形式

  • 1、给函数式接口的变量赋值:多态引用

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

    • new TreeSet( (t1,t2) -> t1.getId() - t2.getId());

5.7 函数式接口

  • 函数式接口也是接口,是个特殊的接口,SAM 型接口

    • SAM:Single Abstract Method

      • 使用注解声明@FunctionInterface
  • 在这个类接口中只有一个抽象方法,但是可以有静态方法和默认方法以及 Object 中的方法

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

  • 四个核心的函数式

    • 1、消费型

      • Consumer void accept(T t)

      • 还有延伸版

        • BiConsumer<T,U>: void accept(T t,U u)
        • 。。。。
    • 2、供给型

      • Supplier 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 apply(T t2, T t2)
        • ….
    • 4、断定型

      • Predicate : boolean test(T t)

      • 延伸版

        • BiPredicate<T,U> boolean test(T t ,U u)
        • ….

5.8 再简化版

  • 方法引用

    • 条件

      • (1)Lambda 体通过调用一个现成的方法来完成
      • (2)Lambda 的形参列表刚好是用于这个方法的实参
    • 形式有三种

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

        • 集合对象.forEach(t -> System.out.println(t));
        • 集合对象.forEach(System.out::println)
      • (2)类名::静态方法名

        • Supplier s = () -> Math.random();

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

          • BiFunction<Integer,Integer,Integer> b = Math::max;
      • (3)类名::实例方法名

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

          • Lambda 表达式的形参的第一个作为调用方法的对象,第二个,作为这个实例方法的实参
          • Comparator 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;

6 StreamAPI

6.1 作用

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

6.2 特点

  • 1、Stream 不负责存储数据
  • 2、Stream 的操作不影响数据源,每次操作返回一个新的 Stream
  • 3、Stream 的中间操作是一个“懒惰求值”,一直要到终结操作,才会一口气完成

6.3 步骤

  • 1、创建 Stream

    - 四种方式
    
    	- 1、Collection集合对象.stream()
    
    		- ArrayList<String> list = new ArrayList<>();
    

    。。。 //创建 stream Stream stream = list.stream();

    	- 2、Arrays.stream(数组对象)
    
    		- String[] arr = {"hello","world","java","lambda","stream"};
    

    Stream 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筛选与切片
    
    	- (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是一个 BiFunction<T,T,T>接口: T apply(T t1, T t2) B:T reduce(T identity,BinaryOperator accumulator)

    - (4)收集
    
    	- <R,A> R collect(Collector<? super T,A,R> collector)
    
    		- Collector 接口中方法的实现决定了如何对流执行收集的操作
    

    Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,例如、List,Set,Map