Lambda&Stream

【主要内容】

  1. Lambda简介

  2. Lambda语法

  3. Lambda学用示例

  4. Stream概述

  5. Stream的基本使用

【学习目标】

知识点要求
Lambda简介掌握
Lambda语法掌握
Lambda常用示例掌握
Stream概述掌握
Stream的基本使用掌握

1. Lambda表达式

1.1 Lambda简介

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。

JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

1.2 对接口的要求

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。

jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。

1.3 @FunctionalInterface

修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。

1.4 Lambda 基础语法

我们这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述。

package com.huayu.lambda;
public class LambdaInterface {
}
/**
 * 无参无返回值
 */
@FunctionalInterface
interface NoReturnNoParam {
    void method();
}
​
/**
 * 一个参数无返回
 */
@FunctionalInterface
interface NoReturnOneParam {
    void method(int a);
}
​
/**
 * 多参数无返回
 */
@FunctionalInterface
interface NoReturnMultiParam {
    void method(int a, int b);
}
​
/**
 * 无参有返回
 */
@FunctionalInterface
interface ReturnNoParam {
    int method();
}
​
/**
 * 一个参数有返回值
 */
@FunctionalInterface
interface ReturnOneParam {
    int method(int a);
}
​
/**
 * 多个参数有返回值
 */
@FunctionalInterface
interface ReturnMultiParam {
    int method(int a, int b);
}

语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

1.5 Lambda 基础语法测试

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description Lambda表达式基础语法测试
*/
public class LambdaDemo1 {
    public static void main(String[] args) {
        // 无参无返回值方法
        NoReturnNoParam noReturnNoParam = () ->{
            System.out.println("无参无返回值方法");
        };
        noReturnNoParam.method();
​
        // 一个参数无返回值方法
        NoReturnOneParam noReturnOneParam = (int a) ->{
            System.out.println("一个参数无返回值");
        };
        noReturnOneParam.method(10);
​
        // 多个参数无返回值方法
        NoReturnMultiParam noReturnMultiParam = (int a , int b) ->{
            System.out.println("多个参数无返回值");
        };
        noReturnMultiParam.method(10,20);
​
        // 无参有返回值方法
        ReturnNoParam returnNoParam = ()->{
            return 100;
        };
        System.out.println(returnNoParam.method());
        // 一个参数有返回值方法
        ReturnOneParam returnOneParam = (int a) ->{
            return a;
        };
        System.out.println(returnOneParam.method(200));
​
        // 多个参数有返回值方法
        ReturnMultiParam returnMultiParam = (int a,int b)->{
            return a+b;
        };
        System.out.println(returnMultiParam.method(100, 200));
    }
}

1.6 Lambda 语法简化测试

我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description Lambda表达式简化版测试
*/
public class LambdaDemo2 {
    public static void main(String[] args) {
        // 无参无返回值方法
        NoReturnNoParam noReturnNoParam = () -> System.out.println("无参无返回值方法");
        noReturnNoParam.method();
​
        // 一个参数无返回值方法
        NoReturnOneParam noReturnOneParam =  a -> System.out.println("一个参数无返回值");
        noReturnOneParam.method(10);
​
        // 多个参数无返回值方法
        NoReturnMultiParam noReturnMultiParam = ( a ,  b) -> System.out.println("多个参数无返回值");
        noReturnMultiParam.method(10,20);
​
        // 无参有返回值方法
        ReturnNoParam returnNoParam = ()-> 100;
        System.out.println(returnNoParam.method());
        // 一个参数有返回值方法
        ReturnOneParam returnOneParam =  a -> a;
        System.out.println(returnOneParam.method(200));
​
        // 多个参数有返回值方法
        ReturnMultiParam returnMultiParam = (a,b)-> a+b;
        System.out.println(returnMultiParam.method(100, 200));
    }
}

1.7 Lambda 表达式常用示例

1.7.1 lambda 表达式引用方法

有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以利用 lambda表达式的接口快速指向一个已经被实现的方法。

语法:

方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象

代码如下所示:

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description Lambda表达式方法的引用
          类中静态方法的引用:   类名::静态方法名;
          类中非静态方法的引用: 对象名::非静态方法名;
*/
public class LambdaDemo3 {
    public static void main(String[] args) {
        // 一个参数有返回值方法
        ReturnOneParam returnOneParam =  a -> a;
        System.out.println(returnOneParam.method(200));
​
        // 引用LambdaDemo3中静态方法staticMethod的方法体
        ReturnOneParam returnOneParam1 = LambdaDemo3::staticMethod;
        int method = returnOneParam1.method(10);
        System.out.println("method = " + method);
​
        // 引用LambdaDemo3中非静态方法method的方法体
        LambdaDemo3 lambdaDemo3 = new LambdaDemo3();
        ReturnOneParam returnOneParam2 = lambdaDemo3::method;
        int method1 = returnOneParam2.method(10);
        System.out.println("method1 = " + method1);
    }
​
    /*
        定义静态方法
           要求: 方法的参数列表和返回值类型必须和接口中被重写方法一致
     */
    public static int staticMethod(int a){
        return a * 2;
    }
​
    /*
         定义非静态方法
             要求: 方法的参数列表和返回值类型必须和接口中被重写方法一致
     */
    public  int method(int a){
        return a + 2;
    }
}

运行结果如图:

 

1.7.2 构造方法的引用

一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description Lambda表达式构造方法的引用
        说明: 使用接口作为对象生成器
​
*/
public class LambdaDemo4 {
    public static void main(String[] args) {
        // 重写NoParamConstructorInter接口中的getStu() , 将 new Student()作为方法体
        NoParamConstructorInter inter1 = () -> new Student();
        Student stu = inter1.getStu();
        System.out.println("stu = " + stu);
​
        // Lambda表达式构造方法的引用
        NoParamConstructorInter inter2 = Student::new;
        // 调用getStu()没有传入任何参数,所以引用的是:Student类中的无参构造方法
        Student stu1 = inter2.getStu();
        System.out.println("stu1 = " + stu1);
​
        MulitParamConstructorInter inter3 = Student::new;
        // 调用getStu()传入三个参数,所以引用的是:Student类中的满参构造方法
        Student stu2 = inter3.getStu("jack", 18, "上海");
        System.out.println("stu2 = " + stu2);
​
    }
}
interface NoParamConstructorInter{
    Student getStu();
}
interface MulitParamConstructorInter{
    Student getStu(String name , int age , String address);
}
class Student{
    private String name;
    private int age;
    private String address;
​
    public Student() {
    }
​
    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public String getAddress() {
        return address;
    }
​
    public void setAddress(String address) {
        this.address = address;
    }
​
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

1.7.3 lambda 表达式创建线程

我们以往都是通过创建 Thread 对象,然后通过匿名内部类重写 run() 方法,一提到匿名内部类我们就应该想到可以使用 lambda 表达式来简化线程的创建过程。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 使用Lambda创建线程
*/
public class LambdaDemo5 {
    public static void main(String[] args) {
        // 1. 使用匿名内部类创建线程
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程任务开始执行.");
            }
        },"线程A");
        t1.start();
        
        //2. 使用Lambda简化写法: 标准写法
        Thread t2 = new Thread(()->{
            System.out.println("线程任务开始执行.");
        });
        t2.start();
​
        //3. 使用Lambda简化写法: 简化写法写法
        Thread t3 = new Thread(()-> System.out.println("线程任务开始执行."));
        t3.start();
    }
}

1.7.4 遍历集合

我们可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。

 

 

代码如下:

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 使用Lambda表达式遍历集合
​
*/
public class LambdaDemo6 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(11,12,13,14,15);
        // 使用Lambda表达式
        list.forEach(item -> System.out.println(item));
​
        System.out.println("------------------------------");
​
        // 使用方法的引用:引用 System.out 中的println(Object x)
        list.forEach(System.out::println);
    }
}

1.7.5 删除集合中的某个元素

我们通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。

 

 

代码如下:

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 使用Lambda表达式删除集合中某一个元素
​
*/
public class LambdaDemo7 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("zhangsan",18,"上海"));
        list.add(new Student("lisi",16,"北京"));
        list.add(new Student("wangwu",22,"南京"));
        list.add(new Student("zhaoliu",30,"武汉"));
        // 删除集合中年龄为16的学生
        list.removeIf(item-> item.getAge() == 16);
​
        // 遍历集合
        list.forEach(System.out::println);
    }
}

1.7.6 集合内元素的排序

在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。

代码如下:

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 使用Lambda表达式给集合中元素排序
​
*/
public class LambdaDemo8 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("zhangsan",18,"上海"));
        list.add(new Student("lisi",16,"北京"));
        list.add(new Student("wangwu",22,"南京"));
        list.add(new Student("zhaoliu",30,"武汉"));
​
        // 使用Lambda表达式给集合中元素排序
        list.sort((item1,item2) -> item1.getAge() - item2.getAge());
​
        // 遍历集合
        list.forEach(System.out::println);
    }
}

2. Stream处理集合

2.1 知识点概述

 

 

2.2 Stream概述

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream可以由数组或集合创建,对流的操作分为两种:

  • 中间操作,每次返回一个新的流,可以有多个

  • 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

  • stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。

  • stream不会改变数据源,通常情况下会产生一个新的集合或一个值

  • stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行

2.3 Stream的创建

Stream可以通过集合数组创建。

1、通过 java.util.Collection.stream() 方法用集合创建流

2、使用java.util.Arrays.stream(T[] array)方法用数组创建流

3、使用Stream的静态方法:of()、iterate()、generate()

代码如下:

package com.huayu.stream;
​
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
​
/*
    @Author xiangge
    @Date 2023/8/23 
    @Description Stream流创建的三种方式
        Stream可以通过集合数组创建。
         1、通过 java.util.Collection.stream() 方法用集合创建流
         2、使用java.util.Arrays.stream(T[] array)方法用数组创建流
         3、使用Stream的静态方法:of()、iterate()、generate()
*/
public class StreamDemo1 {
    public static void main(String[] args) {
        // 1. 通过单列集合创建Stream流
        List<Integer> list = Arrays.asList(11,22,33,44,55,66,77);
        // 通过集合创建一个顺序流
        Stream<Integer> stream1 = list.stream();
        // 将流转换为List集合
        List<Integer> collect1 = stream1.collect(Collectors.toList());
        System.out.println("collect1 = " + collect1);
        System.out.println("-------------------------");
​
        // 通过集合创建一个并行流
        Stream<Integer> stream2 = list.parallelStream();
        List<Integer> collect2 = stream2.collect(Collectors.toList());
        collect2.forEach(System.out::println);
        System.out.println("-------------------------");
​
        // 2. 使用数组创建流
        IntStream stream3 = Arrays.stream(new int[]{11, 22, 33, 44, 55, 66, 77});
        stream3.forEach(System.out::println);
        System.out.println("-------------------------");
​
        // 3. 使用Stream的静态方法:of()、iterate()、generate()创建Stream
        Stream<Integer> stream4 = Stream.of(11, 22, 33, 44, 55, 66, 77);
        stream4.forEach(System.out::println);
        System.out.println("-------------------------");
​
        Stream<Integer> stream5 = Stream.iterate(0, item -> item + 10).limit(3);
        stream5.forEach(System.out::println);
        System.out.println("-------------------------");
​
        Stream<Integer> stream6 = Stream.generate(() -> 10).limit(5);
        stream6.forEach(System.out::println);
        System.out.println("-------------------------");
    }
}

注意:

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。

2.4 Stream的使用

新建实体类Person,后续用于Stream操作。

2.4.1 Person
public class Person {
    private String name;  // 姓名
    private int salary; // 薪资
    private int age; // 年龄
    private String sex; //性别
    private String area;  // 地区
​
    public Person(){}
​
    public Person(String name, int salary,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.sex = sex;
        this.area = area;
    }
​
    // 构造方法
    public Person(String name, int salary, int age,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }
     //get and set 自己写
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getSalary() {
        return salary;
    }
​
    public void setSalary(int salary) {
        this.salary = salary;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public String getSex() {
        return sex;
    }
​
    public void setSex(String sex) {
        this.sex = sex;
    }
​
    public String getArea() {
        return area;
    }
​
    public void setArea(String area) {
        this.area = area;
    }
​
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}

2.4.2 遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 遍历/匹配(foreach/find/match)
*/
public class StreamDemo2 {
    public static void main(String[] args) {
        // 1. 定义集合
        List<Integer> list = Arrays.asList(3, 6, 4, 2, 1, 5, 9, 7, 8);
​
        // 2. 顺序打印
        list.stream().forEach(System.out::println);
        System.out.println("-------------------------------");
        // 3. 并行打印
        list.parallelStream().forEach(System.out::println);
        System.out.println("-------------------------------");
​
        // 4. 发现集合中大于6的元素中的第一个
        Optional<Integer> first = list.stream().filter(item -> item > 6).findFirst();
        System.out.println(first.get());
​
        // 5. 发现集合中大于6的元素中的任意一个
        Optional<Integer> any = list.parallelStream().filter(item -> item > 6).findAny();
        System.out.println(any.get());
​
        // 6. 判断集合中是否包含大于8的元素
        boolean b = list.stream().anyMatch(item -> item > 8);
        System.out.println("b = " + b);
    }
}

2.4.3 筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 遍历/匹配(foreach/find/match)
*/
public class StreamDemo3 {
    public static void main(String[] args) {
        // 1. 定义集合
        List<Integer> list = Arrays.asList(3, 6, 4, 2, 1, 5, 9, 7, 8);
​
        // 2. 筛选集合list中大于5的元素并返回新的集合
        List<Integer> collect = list.stream().filter(item -> item > 5).collect(Collectors.toList());
        collect.forEach(System.out::println);
        System.out.println("-------------------------------------");
​
        // 3. 定义集合
        List<Person> list2 = new ArrayList<>();
        list2.add(new Person("zhangsan",6000,"男","北京"));
        list2.add(new Person("lisi",5500,"男","上海"));
        list2.add(new Person("wangwu",10000,"女","武汉"));
        list2.add(new Person("zhaoliu",4800,"女","长沙"));
        list2.add(new Person("zhangsan",7200,"男","深圳"));
​
        // 4. 筛选出list2中薪资大于7000的元素并返回新的集合
        List<Person> collect1 = list2.stream().filter(item -> item.getSalary() > 7000).collect(Collectors.toList());
        collect1.forEach(System.out::println);
    }
}

2.4.4 聚合(max/min/count)

Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。

/*
    @Author xiangge
    @Date 2023/8/23 
    @Description 聚合(max/min/count)
*/
public class StreamDemo4 {
    public static void main(String[] args) {
        // 1. 定义集合
        List<Integer> list = Arrays.asList(3, 6, 4, 2, 1, 5, 9, 7, 8);
​
        // 2. 获取list中的最大值 自然排序
        Optional<Integer> max = list.stream().max((item1, item2) -> item1 - item2);
        System.out.println(max.get());
​
        // 3. 获取list中的最小值  自然排序
        Optional<Integer> min = list.stream().min((item1, item2) -> item1 - item2);
        System.out.println(min.get());
​
        // 4. 获取list中的最小值 自定义排序规则
        Optional<Integer> min1 = list.stream().min(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(min1.get());
​
        // 定义集合
        List<String> list2 = Arrays.asList("java","H5","python","php","helloworld");
        //5. 获取list2中长度最长的字符串
        Optional<String> max1 = list2.stream().max((item1, item2) -> item1.length() - item2.length());
        System.out.println(max1.get());
​
        //6. 获取list2中长度最短的字符串
        Optional<String> min2 = list2.stream().min(String::compareTo);
        System.out.println(min2.get());
​
        //7. 统计list2中元素的个数
        long count = list2.stream().count();
        System.out.println("count = " + count);
​
        // 8. 定义集合
        List<Person> list3 = new ArrayList<>();
        list3.add(new Person("zhangsan",6000,"男","北京"));
        list3.add(new Person("lisi",5500,"男","上海"));
        list3.add(new Person("wangwu",10000,"女","武汉"));
        list3.add(new Person("zhaoliu",4800,"女","长沙"));
        list3.add(new Person("tianqi",7200,"男","深圳"));
​
        // 9. 获取list3中薪资最高的元素
        Optional<Person> max2 = list3.stream().max((item1, item2) -> item1.getSalary() - item2.getSalary());
        System.out.println(max2.get());
​
        // 10. 获取list3中 姓名最长的元素
        Optional<Person> max3 = list3.stream().max((item1, item2) -> item1.getName().length() - item2.getName().length());
        System.out.println(max3.get());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值