jdk8之lambda表达式&stream流式编程优雅的操作集合以及示例代码,抓紧收藏起来吧

好文推荐:
2.5万字讲解DDD领域驱动设计(史上最全DDD)
2.5万字详解23种设计模式
netty搭建websocket集群(高性能.,高并发)
springboot 实现延时队列(超级实用)
一款基于AI编程的代码自动生成工具Cursor,替代VSCode

在这里插入图片描述

stream流概念

简单来讲,Stream流是一种用于处理数据集合的高级迭代器,它可以对集合中的元素进行各种操作,如过滤、映射、排序等。通过使用Stream API,我们可以以声明式的方式处理数据,而无需显式地编写循环和条件语句。
Stream流的操作分为两种,中间操作终端操作

中间操作

是指对每个元素独立进行操作,不依赖于其他元素的状态。它们不会改变流中的元素本身,而是创建一个新的Stream对象来表示转换后的结果。常见的无状态中间操作有map、filter、flatMap等。

无状态操作:

无状态操作不会改变流中的元素,也不会改变流的状态;这些操作可以并行执行,因为它们不依赖于流中的其他元素。例如:

  • distinct: 返回去重的Stream。
  • limit: 限制从流中获得前n个数据,返回前n个元素数据组成的Stream流。
  • skip: 跳过前n个数据,返回第n个元素后面数据组成的Stream。
  • sorted: 返回一个排序的Stream。

有状态操作

有状态操作会改变流的状态,或者依赖于流中的其他元素。这些操作不能并行执行,因为它们需要访问或修改流的状态。
例如:

  • filter: 过滤流,过滤流中的元素,返回一个符合条件的Stream
  • map: 转换流,将一种类型的流转换为另外一种流。(mapToInt、mapToLong、mapToDouble 返回int、long、double基本类型对应的Stream)
  • peek:主要用来查看流中元素的数据状态,该方法主要用于调试,方便debug查看Stream内进行处理的每个元素。仅在对流内元素进行操作时,peek才会被调用,当不对元素做任何操作时,peek自然也不会被调用了
  • flatMap: 简单的说,就是一个或多个流合并成一个新流。

终端操作

是对数据进行最终处理的操作,它们会消耗掉Stream并产生一个结果或者副作用(如输出到控制台)。一旦执行了终端操作,Stream就不能再被使用。常见的终端操作有collect、forEach、reduce等。

非短路操作:

非短路操作会处理流中的所有元素,并返回一个结果。
如:

  • forEach: 循环操作Stream中数据。
  • toArray: 返回流中元素对应的数组对象。
  • reduce: 聚合操作,用来做统计,将流中元素反复结合起来统计计算,得到一个值.。
  • collect: 聚合操作,封装目标数据,将流转换为其他形式接收,如: List、Set、Map、Array。
  • min、max、count: 聚合操作,最小值,最大值,总数量。

短路操作:

短路操作会在满足某个条件时提前结束处理,并返回一个结果。例如:

  • anyMatch: 短路操作,有一个符合条件返回true。
  • allMatch: 所有数据都符合条件返回true。
  • noneMatch: 所有数据都不符合条件返回true。
  • findFirst: 短路操作,获取第一个元素。
  • findAny: 短路操作,获取任一元素。

steam流的生成

方式一:

数组转为stream流

		int [] arr = {1,2,3,4,5,6,7,8,9,10};
        Arrays.stream(arr).forEach(
                System.out::println
        );

方式二:

集合转为steam流

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
list.stream().forEach(System.out::println);

方式三:

Stream.builder创建stream流,允许你逐步构建一个流

Stream.Builder<Integer> builder = Stream.builder();

// 添加元素到流中
builder.add(1);
builder.add(2);
builder.add(3);

// 构建流
Stream<Integer> stream = builder.build();

// 使用流
stream.forEach(System.out::println);

方式四:

使用 Stream.of 方法,Stream.of 方法可以接受一系列元素并返回一个包含这些元素的流。

Stream<String> words = Stream.of("apple", "banana", "orange");
words.forEach(System.out::println);

方式五:

从文件创建流,可以使用 Files.lines 方法从文件中创建流。

try (Stream<String> stream = Files.lines(Paths.get("file.txt"))) {
    stream.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

方式六:

生成无限流,可以使用 Stream.generate 或 Stream.iterate 方法来生成无限流。

Stream<Double> randomNumbers = Stream.generate(Math::random);
randomNumbers.forEach(System.out::println);
Stream<Integer> oddNumbers = Stream.iterate(1, n -> n + 2);
oddNumbers.forEach(System.out::println);

无状态的中间操作

distinct

返回一个去重的流,即去除重复的元素

Stream<Integer> distinctStream = Stream.of(1, 2, 2, 3, 4, 4, 5).distinct();
distinctStream.forEach(System.out::println);
//输出结果:12345

limit

限制从流中获得前n个数据,返回前n个元素数据组成的流

Stream<Integer> limitedStream = Stream.of(1, 2, 3, 4, 5).limit(3);
limitedStream.forEach(System.out::println);
//输出结果:123

skip

Stream<Integer> skippedStream = Stream.of(1, 2, 3, 4, 5).skip(2);
skippedStream.forEach(System.out::println);
//输出结果:345

sorted

//正序排序从小到大
Stream<Integer> sortedStream = Stream.of(5, 3, 1, 4, 2).sorted();
sortedStream.forEach(System.out::println);
//输出结果:12345

//逆序排序从大到小,Comparator.reverseOrder() 创建了一个逆序比较器,然后传递给 sorted 方法,从而实现了逆序排序
Stream<Integer> sortedStream = Stream.of(5, 3, 1, 4, 2).sorted(Comparator.reverseOrder());
sortedStream.forEach(System.out::println);
//输出结果:54321

组合使用

这段代码首先去重,然后排序,跳过第一个元素,最后限制结果流只包含前三个元素。

Stream<Integer> stream = Stream.of(1, 2, 2, 3, 4, 4, 5)
                .distinct()
                .sorted()
                .skip(1)
                .limit(3);
stream.forEach(System.out::println);
//输出结果:234

测试实体类:

public class Person {
    private int id;
    private String name; // 姓名
    private int salary; // 薪资
    private int age; // 年龄
    private String sex; //性别
    private String area; // 地区
    private List<Person> employeeList; //下属

    public Person() {
    }

    // 构造方法
    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;
    }

    // 构造方法
    public Person(int id, String name, int salary, int age, String sex, String area) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }
    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;
    }

    public List<Person> getEmployeeList() {
        return employeeList;
    }

    public void setEmployeeList(List<Person> employeeList) {
        this.employeeList = employeeList;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}
// 创建一个List来存储Person对象
List<Person> personList = new ArrayList<>();

// 创建8个Person对象并添加到列表中
personList.add(new Person(1, "Alice", 5000, 30, "Female", "New York"));
personList.add(new Person(2, "Bob", 6000, 35, "Male", "Los Angeles"));
personList.add(new Person(3, "Charlie", 5500, 28, "Male", "Chicago"));
personList.add(new Person(4, "Diana", 7000, 40, "Female", "Miami"));
personList.add(new Person(5, "Ethan", 4800, 25, "Male", "Houston"));
personList.add(new Person(6, "Fiona", 5300, 32, "Female", "Seattle"));
personList.add(new Person(7, "George", 6200, 38, "Male", "Boston"));
personList.add(new Person(8, "Hannah", 5900, 29, "Female", "San Francisco"));

有状态的中间操作

filter(重要)

		// 1. 筛选出所有女性
        List<Person> females = personList.stream()
                .filter(person -> person.getSex().equalsIgnoreCase("female"))
                .collect(Collectors.toList());
        System.out.println("女性列表: " + females);

        // 2. 筛选出薪资高于5000的人员
        List<Person> highSalary = personList.stream()
                .filter(person -> person.getSalary() > 5000)
                .collect(Collectors.toList());
        System.out.println("薪资高于5000的人员: " + highSalary);

        // 3. 筛选出年龄在30岁及以上的人员
        List<Person> ageAbove30 = personList.stream()
                .filter(person -> person.getAge() >= 30)
                .collect(Collectors.toList());
        System.out.println("年龄在30岁及以上的人员: " + ageAbove30);

        // 4. 筛选出居住在特定城市(例如"New York")的人员
        List<Person> livingInNewYork = personList.stream()
                .filter(person -> person.getArea().equalsIgnoreCase("New York"))
                .collect(Collectors.toList());
        System.out.println("居住在纽约的人员: " + livingInNewYork);

        // 5. 筛选出名字以"A"开头的人员
        List<Person> namesStartingWithA = personList.stream()
                .filter(person -> person.getName().startsWith("A"))
                .collect(Collectors.toList());
        System.out.println("名字以A开头的人员: " + namesStartingWithA);

map(重要)

        // 1. 提取所有人的名字
        List<String> names = personList.stream()
                .map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("所有人的名字: " + names);

        // 2. 提取所有人的薪资
        List<Integer> salaries = personList.stream()
                .map(Person::getSalary)
                .collect(Collectors.toList());
        System.out.println("所有人的薪资: " + salaries);

        // 3. 提取所有人的地区
        List<String> cities = personList.stream()
                .map(Person::getArea)
                .collect(Collectors.toList());
        System.out.println("所有人的城市: " + cities);

        // 4. 提取所有人的年龄,并加上10
        List<Integer> agesPlus10 = personList.stream()
                .map(person -> person.getAge() + 10)
                .collect(Collectors.toList());
        System.out.println("所有人的年龄加十: " + agesPlus10);

        // 5. 提取薪资信息并格式化为字符串
        List<String> salaryInfo = personList.stream()
                .map(person -> person.getName() + "的薪资为: " + person.getSalary())
                .collect(Collectors.toList());
        System.out.println("薪资信息: " + salaryInfo);

flatMap

flatMap 是 Java Stream API 中的一个非常有用的方法,通常用于将多个流扁平化为一个流。在处理 Person 对象时,我们可以使用 flatMap 来进行一些复杂的数据结构操作。以下是一些示例,展示如何在你的 personList 上使用 flatMap。

List<String> flatMappedList = personList.stream()
                                        .map(person -> person.getName()) // 将Person对象转换为名字
                                        .flatMap(name -> Stream.of(name, name.toUpperCase())) // 将名字转换为名字和名字的大写形式
                                        .collect(Collectors.toList());

peek

personList.stream()
          .peek(person -> System.out.println("Before filter: " + person)) // 打印每个Person对象
          .filter(person -> person.getAge() > 30) // 过滤年龄大于30的Person对象
          .peek(person -> System.out.println("After filter: " + person)) // 打印过滤后的Person对象
          .collect(Collectors.toList());

组合使用

// 使用Stream API处理personList
List<String> combinedList = personList.stream()
                                      .filter(person -> person.getAge() > 30) // 过滤年龄大于30的Person对象
                                      .map(person -> person.getName()) // 将Person对象转换为名字
                                      .flatMap(name -> Stream.of(name, name.toUpperCase())) // 将名字转换为名字和名字的大写形式
                                      .peek(System.out::println) // 打印每个名字
                                      .collect(Collectors.toList()); // 收集结果到一个List中

非短路的终端操作

forEach

personList.stream()
          .forEach(person -> System.out.println(person.getName()));

personList.stream()
          .forEachOrdered(person -> System.out.println(person.getName()));

toArray

Object[] array = personList.stream()
                           .toArray();

reduce(重要)

		// 1. 计算总薪资
        int totalSalary = personList.stream()
                .map(Person::getSalary)
                .reduce(0, Integer::sum);
        System.out.println("总薪资: " + totalSalary);

        // 2. 计算平均薪资
        Optional<Double> averageSalary = personList.stream()
                .map(Person::getSalary)
                .reduce((a, b) -> a + b)
                .map(sum -> sum / (double) personList.size());
        System.out.println("平均薪资: " + averageSalary.orElse(0.0));

        // 3. 查找最高薪资
        Optional<Integer> maxSalary = personList.stream()
                .map(Person::getSalary)
                .reduce(Integer::max);
        System.out.println("最高薪资: " + maxSalary.orElse(0));

        // 4. 查找最低薪资
        Optional<Integer> minSalary = personList.stream()
                .map(Person::getSalary)
                .reduce(Integer::min);
        System.out.println("最低薪资: " + minSalary.orElse(0));

        // 5. 计算年龄之和
        int totalAge = personList.stream()
                .map(Person::getAge)
                .reduce(0, Integer::sum);
        System.out.println("总年龄: " + totalAge);

collect(重要)

        //收集到 List集合
        List<String> names = personList.stream()
                .map(Person::getName)
                .collect(Collectors.toList());
        //收集到 Set集合
        Set<String> cities = personList.stream()
                .map(Person::getArea)
                .collect(Collectors.toSet());
        //收集到 Map集合
        Map<Integer, String> idToNameMap = personList.stream()
                .collect(Collectors.toMap(Person::getId, Person::getName));

        Map<Integer, Person> idToPersonMap = personList.stream()
                .collect(Collectors.toMap(Person::getId, v -> v));
        //收集到自定义集合
        List<Person> sortedList = personList.stream()
                .sorted(Comparator.comparing(Person::getAge))
                .collect(Collectors.toList());
        //使用 Collectors.joining 连接字符串
        String namesString = personList.stream()
                .map(Person::getName)
                .collect(Collectors.joining(", "));
        //Collectors.groupingBy
        Map<String, List<Person>> cityToPeopleMap = personList.stream()
                .collect(Collectors.groupingBy(Person::getArea));
        //使用 Collectors.partitioningBy 分区
        Map<Boolean, List<Person>> isAdultMap = personList.stream()
                .collect(Collectors.partitioningBy(person -> person.getAge() >= 18));
        //使用 Collectors.summarizingInt 计算统计信息
        IntSummaryStatistics salarySummary = personList.stream()
                .collect(Collectors.summarizingInt(Person::getSalary));
        double average = salarySummary.getAverage();//平均值
        int max = salarySummary.getMax();//最大值
        long count = salarySummary.getCount();//计数
        int min = salarySummary.getMin();//最小值
        long sum = salarySummary.getSum();//求和

min、max、count

Optional<Person> maxSalaryPerson = personList.stream()
                                             .max(Comparator.comparing(Person::getSalary));
Optional<Person> minAgePerson = personList.stream()
                                          .min(Comparator.comparing(Person::getAge));
 long count = personList.stream().count();
                                           

组合使用

这段代码首先计算所有 Person 对象的工资总和,然后找到年龄最大的 Person 对象,最后将所有 Person 对象的名字收集到一个 List 中。

int totalSalary = personList.stream()
                            .mapToInt(Person::getSalary)
                            .reduce(0, Integer::sum);

Optional<Person> oldestPerson = personList.stream()
                                         .max(Comparator.comparing(Person::getAge));

List<String> names = personList.stream()
                               .map(Person::getName)
                               .collect(Collectors.toList());

短路的终端操作

anyMatch

boolean hasFemale = personList.stream()
                              .anyMatch(person -> "Female".equals(person.getGender()));

allMatch

boolean allAdults = personList.stream()
                              .allMatch(person -> person.getAge() >= 18);

noneMatch

boolean noRetired = personList.stream()
                              .noneMatch(person -> person.getAge() >= 65);

findFirst

Optional<Person> firstPerson = personList.stream()
                                         .findFirst();

findAny

Optional<Person> anyPerson = personList.stream()
                                       .findAny();

组合使用

这段代码首先检查是否存在女性,然后找到第一个成年人,最后将所有成年人的名字收集到一个 List 中。

boolean hasFemale = personList.stream()
                              .anyMatch(person -> "Female".equals(person.getGender()));

Optional<Person> firstAdult = personList.stream()
                                        .filter(person -> person.getAge() >= 18)
                                        .findFirst();

List<String> names = personList.stream()
                               .filter(person -> person.getAge() >= 18)
                               .map(Person::getName)
                               .collect(Collectors.toList());

并行流

并行流可以提高处理大数据集时的性能。Java Stream API 的并行处理是基于 Java 的 Fork/Join 框架实现的。Fork/Join 框架是 Java 7 引入的一种并行计算框架,它可以将一个大任务拆分成多个小任务,然后在多个处理器上并行执行这些小任务,最后将结果合并。

在 Stream API 中,并行流是通过 parallelStream() 方法创建的。当你调用 parallelStream() 方法时,Stream API 会创建一个 ForkJoinTask,并将其提交给 ForkJoinPool 执行。ForkJoinPool 是一个特殊的线程池,它使用工作窃取算法来平衡任务执行,从而提高并行处理效率。

String allNames = personList.parallelStream()
    .map(Person::getName)
    .collect(Collectors.joining(", "));
System.out.println("All Names (Parallel): " + allNames);

总结

Stream API 的主要特点包括:

  1. 简洁性:Stream API 提供了一种简洁的方式来处理集合数据,使得代码更加易读、易写。

  2. 可读性:Stream API 的操作可以链式调用,使得代码更加清晰、易读。

  3. 并行处理:Stream API 支持并行处理,可以充分利用多核处理器的能力。

  4. 惰性求值:Stream API 的操作是惰性求值的,即只有在需要结果时才会执行操作。

  5. 无状态操作:Stream API 的无状态操作不会改变流中的元素,也不会改变流的状态。

  6. 有状态操作:Stream API 的有状态操作会改变流的状态,或者依赖于流中的其他元素。

  7. 短路操作:Stream API 的短路操作会在满足某个条件时提前结束处理,并返回一个结果。

  8. 终端操作:Stream API 的终端操作会处理流中的所有元素,并返回一个结果。

如果看到这里,说明你喜欢这篇文章,请转发,点赞。关注【微信公众号】微信搜索【老板来一杯java】
回复【进群】即可获取【DDD源码】,并赠送【DDD领域驱动设计实战落地解惑】PDF一份!
回复【java】即可获取【java基础经典面试】一份!

好文推荐:
2.5万字讲解DDD领域驱动设计(史上最全DDD)
2.5万字详解23种设计模式
netty搭建websocket集群(高性能.,高并发)
springboot 实现延时队列(超级实用)
史上最全从0到1搭建最新版本jenkins可持续集成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王德印

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值