day22_JDK8的新特性(Lambda表达式丶Stream流)

Lambda表达式

面向对象编程思想

面向对象强调的是对象 , 必须通过对象的形式来做事情,相对来讲比较复杂,有时候我们只是为了做某件事情而不得不创建一个对象 , 例如线程执行任务,我们不得不创建一个实现Runnable接口对象,但我们真正希望的是将run方法中的代码传递给线程对象执行

函数编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。例如线程执行任务 , 使用函数式思想 , 我们就可以通过传递一段代码给线程对象执行,而不需要创建任务对象

函数式编程思想强调做什么,而不是以什么形式做,也就是直接传入一段代码,不需要创建对象。Lambda表达式就是函数编程思想的体现

Lambda的前提条件

用Lambda必须具有接口,且要求接口中的抽象方法有且仅有一个。(别的方法没有影响) (条件)。使用Lambda必须具有上下文推断。如果一个接口中只有一个抽象方法,那么这个接口叫做是函数式接口。 @FunctionalInterface这个注解 就表示这个接口是一个函数式接口。

Lambda表达式的标准格式:  

Lambda表达式的作用:

  • 就是简化代码,省略了面向对象中类和方法,对象的书写。

Lambda表达式的格式说明:

  • 小括号中的参数要和接口中抽象方法的形参列表一致,无参数则留空;多个参数则用逗号分隔。
  • ->是新引入的语法格式,代表指向动作。可以理解为把小括号中的参数传递给大括号中使用
  • 大括号中的内容其实就是存放以前重写抽象方法的方法体

使用Lambda表达式步骤:

  1. 判断接口是否是函数式接口
  2. 如果是函数式接口,那么就直接写()->{}
  3. 然后填充小括号和大括号中的内容

Lambda表达式省略格式:

  • 小括号中的形参类型可以省略
  • 如果小括号中只有一个参数,那么小括号也可以省略
  • 如果大括号中只有一条语句,那么大括号,分号,return可以一起省略

Lambda的表现形式:

  • 变量的形式:变量的类型为函数式接口类型,那么可以赋值一个Lambda表达式
  • 参数的形式:方法的形参类型为函数式接口类型,那么就可以传入一个Lambda表达式           
  • 返回值的形式:方法的返回值类型为函数式接口类型,那么就可以返回一个Lambda表达式    

代码示例

package demo06;


import java.util.ArrayList;
import java.util.Collections;

@FunctionalInterface
interface A {
    void method(int num);
}

public class Test {

    public static void show(A a) {
        a.method(10);
    }

    public static void main(String[] args) {
        /*
            Lambda表达式省略格式:
                1.小括号中的形参类型可以省略
                2.如果小括号中只有一个参数,那么小括号也可以省略
                3.如果大括号中只有一条语句,那么大括号,分号,return可以一起省略
         */
        // 案例1:创建线程执行任务
        new Thread(() ->
                System.out.println("任务代码")
        ).start();

        // 案例2: 对ArrayList集合元素进行排序
        ArrayList list = new ArrayList<>();
        list.add(300);
        list.add(200);
        list.add(100);
        list.add(500);
        list.add(400);
        System.out.println("排序前:" + list);

        // 对集合中的元素按照降序排序

        // 函数式编程:Lambda表达式
        Collections.sort(list, (i1, i2) -> i2 - i1);

        System.out.println("排序后:" + list);

        System.out.println("=======================================");
        // Lambda标准格式
        show((int num) -> {
            System.out.println(num);
        });

        // Lambda省略格式
        show(num ->
                System.out.println(num)
        );
    }
}

Stream流

说到Stream便容易想到I/O Stream,而实际上,谁规定就一定是“IO呢?在Java 8中,得益于Lambda所带 来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

整体来看,流式思想类似于工厂车间的生产流水线”。当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个模型步骤方案,然后再按照方案去执行它。

Stream(流)是一个来自数据源的元素队列 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。 数据源 流的来源。 可以是集合,数组 等。 和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(flfluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)
  • 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭 代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。

当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以 像链条一样排列,变成一个管道。

总结:

  • 搭建好函数模型,才可以执行函数模型:
  • 一定要有终结的方法,没有终结的方法,这个函数模型是不会执行的
  • Stream流的操作方式也是流动操作的,也就是说每一个流都不会存储元素
  • 一个Stream流只能操作一次,不能重复使用
  • Stream流操作不会改变数据源

1:获取流

  • Collection接口中有一个stream()方法,可以获取流 , default Stream stream():获取一个Stream流

代码示例

package demo07;

import java.util.*;
import java.util.stream.Stream;

public class Test {
    public static void main(String[] args) {
        // 创建List集合
        List list = new ArrayList<>();
        Stream stream1 = list.stream();  //获取list集合的流

        // 创建Set集合
        Set set = new HashSet<>();
        Stream stream2 = set.stream();   //获取set集合的流

        // 创建Map集合
        Map map = new HashMap<>();
        // 1.根据Map集合的键获取流
        Set keys = map.keySet();
        Stream stream3 = keys.stream();

        // 2.根据Map集合的值获取流
        Collection values = map.values();
        Stream stream4 = values.stream();

        // 3.根据Map集合的键值对对象获取流
        Set> entrys = map.entrySet();
        Stream> stream5 = entrys.stream();
    }
}

数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:
代码示例
        // 根据数组获取流
        String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
        Stream stream = Stream.of(array);

2:常用方法 

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  • 延迟方法返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方 法均为延迟方法。)
  • 终结方法返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调 用。最常用的终结方法包括 :count 和 forEach 方法。

常用的方法如下所示

day22_JDK8的新特性(Lambda表达式丶Stream流)_第1张图片

逐一处理
package demo08;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
         /*
            forEach方法:
                void forEach(Consumer action);逐一处理流中的元素
                参数Consumer: 函数式接口,抽象方法void accept(T t);
         */
        List list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(""+i);
        }
        // 函数模型: 获取流->逐一消费流中的元素。该方法保证元素的逐一消费动作在流中是被有序执行的。
        list.stream().forEach((String e)->{
            System.out.println(e);
        });

        System.out.println("========================================");
        // 并行流: 通过Collection的parallelStream()方法可以得到并行流,不保证元素的逐一消费动作在流中是被有序执行的。
        list.parallelStream().forEach((String e)->{
            System.out.println(e);
        });
    }
}


统计个数

package demo08;

import java.util.ArrayList;
import java.util.List;

public class Test2_count {
    public static void main(String[] args) {
        /*
            count方法:
            long count();统计流中元素的个数
         */
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张杰");
        list.add("张三丰");

        long count = list.stream().count();
        System.out.println("流中元素的个数:"+count);// 5
    }
}

过滤

import java.util.stream.Stream;

public class Test3_filter {
    /*
        filter方法:
            Stream filter(Predicate predicate);过滤出满足条件的元素
            参数Predicate: 函数式接口, 抽象方法 boolean test(T t);
            Predicate接口:是一个判断接口
     */
    public static void main(String[] args) {
        // 获取Stream流
        Stream stream = Stream.of("张三丰", "张无忌", "灭绝师太", "周芷若", "张翠山", "殷素素");
        // 需求:过滤出姓张的元素
        stream.filter((String s) -> {
            return s.startsWith("张");
        }).forEach((String name)->{
            System.out.println(name);
        });

    }
}

取用前几个

package demo08;

import java.util.stream.Stream;

public class Test2_count {
    public static void main(String[] args) {
      /*
            limit方法:
                Stream limit(long maxSize);取用前几个
                注意:参数是一个long型,如果流的当前长度大于参数则进行截取;否则不进行操作
         */
        // 获取Stream流
        Stream stream = Stream.of("张三丰", "张无忌", "灭绝师太", "周芷若", "张翠山", "殷素素");

        // 需求: 保留前3个元素
        stream.limit(3).forEach(name-> System.out.println(name));
        System.out.println("===============================");

        //注意:参数是一个long型,如果流的当前长度大于参数则进行截取;否则不进行操作

        // 获取Stream流
        Stream stream1 = Stream.of("张三丰", "张无忌", "灭绝师太", "周芷若", "张翠山", "殷素素");

        // 需求: 保留前7个元素,如果流的当前长度大于参数则进行截取;否则不进行操作
        stream1.limit(7).forEach(name-> System.out.println(name));
    }
}

跳过前几个

package demo08;

import java.util.stream.Stream;

public class Test2_count {
    public static void main(String[] args) {
  /*
            skip方法:
                Stream skip(long n);跳过前几个元素
                如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
         */
        // 获取Stream流
        Stream stream = Stream.of("张三丰", "张无忌", "灭绝师太", "周芷若", "张翠山", "殷素素");

        // 需求: 跳过前3个元素
        stream.skip(3).forEach(name -> System.out.println(name));
    }
}

map:映射

        /*
            map方法:
                 Stream map(Function mapper);
                参数Function: 函数式接口,抽象方法 R apply(T t);
                Function其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)
         */
        // 获取流
        Stream stream1 = Stream.of("10", "20", "30", "40");
        // 需求:把stream1流中的元素转换为int类型
        stream1.map((String s)->{return Integer.parseInt(s);}).forEach((Integer i)->{
            System.out.println(i+1);
        });

        System.out.println("=========================");
        // 获取流
        Stream stream2 = Stream.of("10", "20", "30", "40");
        // 需求:把stream1流中的元素转换为String类型
        stream2.map((String s)->{return s+"itheima";}).forEach((String i)->{
            System.out.println(i+1);
        });

concat:组合

        /*
            concat方法:
                static  Stream concat(Stream a, Stream b);合并2个流
         */
        // 获取流
        Stream stream1 = Stream.of("10", "20", "30", "40");

        // 获取Stream流
        Stream stream2 = Stream.of("张三丰", "张无忌", "灭绝师太", "周芷若", "张翠山", "殷素素");

        // 需求:合并stream1和stream2
        Stream stream = Stream.concat(stream1, stream2);
        stream.forEach(name-> System.out.println(name));

对流操作完成之后,如果需要将其结果进行收集,例如获取对应的集合、数组等,如何操作?

收集到数组中

Stream提供toArray方法来将结果放到一个数组中,返回值类型是Object[ ]的:

package demo08;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class Demo {
    public static void main(String[] args) {
          /*
            收集到数组中:
                Stream流的一个方法:
                    Object[] toArray() 返回一个包含此流的元素的数组。
         */
        // 传统方式操作集合:
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张杰");
        list.add("张三丰");
        // 需求:过滤过滤出姓张的并且长度为3的元素
        Stream stream = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3);

        // 需求:把stream流中的元素收集到数组中
        Object[] arr = stream.toArray();
        System.out.println(Arrays.toString(arr));
    }
}

收集到集合中

package demo08;



import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo {
    public static void main(String[] args) {
        /*
            Stream流中提供了一个方法,可以把流中的数据收集到单列集合中:
                 R collect(Collector collector): 把流中的数据收集到单列集合中
                    返回值类型是R,也就是说R指定为什么类型,就是收集到什么类型的集合
                    参数Collector中的R类型: 决定把流中的元素收集到哪个集合中

                - 参数Collector如何得到? 使用java.util.stream.Collectors工具类中的静态方法:
                    - public static  Collector> toList():转换为List集合。
                    - public static  Collector> toSet():转换为Set集合。

         */
        // 传统方式操作集合:
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张杰");
        list.add("张三丰");
        // 需求:过滤过滤出姓张的并且长度为3的元素
        Stream stream1 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3);

        // 收集到List单列集合中
        List list1 = stream1.collect(Collectors.toList());
        System.out.println(list1);// [张无忌, 张三丰]

        
        // 需求:过滤过滤出姓张的并且长度为3的元素
        Stream stream2 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3);
        // 收集到Set单列集合中
        Set set = stream2.collect(Collectors.toSet());
        System.out.println(set);// [张无忌, 张三丰]
    }
}

你可能感兴趣的:(#,一:JavaSE,java)