Java 8 Stream的介绍及使用

一、Stream简介

Java 8 引入了 Stream API,它提供了一种新的处理集合和数组的方式。Stream(流)是一个用于处理数据集合的抽象概念,它可以进行各种操作来处理和转换数据。下面是对 Java 中的 Stream 的详细解释:

  1. 流的创建:流可以从各种数据源创建,包括集合、数组、I/O通道、生成器等。常见的创建流的方式是通过集合的 stream() 方法或数组的 Arrays.stream() 方法。
  2. 中间操作:中间操作是对流进行转换和处理的操作,它们可以按需应用于流上。常见的中间操作包括过滤、映射、排序、去重、限制元素数量等。这些操作并不会立即执行,而是返回一个新的流。
  3. 终端操作:终端操作是对流最终结果的处理操作,它会触发流的遍历和计算。常见的终端操作包括收集结果到集合或数组、聚合操作(如求和、最大值、最小值)、遍历元素等。
  4. 惰性求值:流的中间操作是惰性求值的,只有在执行终端操作时才会触发中间操作的执行。这种特性使得可以对大型数据集进行高效的操作,只计算必要的结果。
  5. 并行流:流支持并行处理,可以通过 parallel() 方法将流转换为并行流。在并行流中,操作会被自动并行化,以提高处理大数据量的效率。
  6. 流的不可变性:流的操作不会改变源数据,而是返回一个新的流。这种不可变性使得流操作更安全和可靠,并且可以进行链式操作。
  7. 可消费性:流只能被遍历或消费一次,一旦遍历完成或终端操作完成,流就无法再使用。如果需要再次对同一数据集进行处理,需要重新创建新的流。

二、使用流程

Java Stream的使用流程通常由三个步骤组成:创建Stream、对Stream进行中间操作、对Stream进行终止操作。其中,中间操作可以是过滤、映射、排序等,终止操作可以是计数、查找、归约等。

1、将集合转换为Stream流(或者创建流)。

2、操作Stream流(中间操作,终端操作)。

stream流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

三、Stream的创建

生成流的方式主要有五种

1、Stream调用自身方法生成

Stream<Integer> stringStream = Stream.of(1,2 , 3);

2、集合类对象创建(常用)

List<Integer> list = new ArrayList<>();

list.add(1);

list.add(2);

list.add(3);

Stream<String> stream = list.stream();

3、Array数组创建

String[] stringArr = {"1", "2", "3"};

Stream<String> streamStr = Arrays.stream(stringArr);

注:使用数值流可以避免计算过程中拆箱装箱,提高性能。

Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流

4、文件创建

Stream<String> fileStream = Files.lines(Paths.get("file.txt"), Charset.defaultCharset());

5、函数创建

Iterator 返回由函数迭代应用于初始元素seed产生的无限有序流

Stream<Integer> iterate = Stream.iterate(0, n -> n + 1).limit(10);

iterate方法接受两个参数,第一个为初始化值,第一次运行时n=0,第二个为进行的函数操作,iterator生成的流为无限流,需要通过limit方法对流进行了截断,最后该代码生产1-10。

四、操作符

流的操作类型主要分为两种:中间操作符、终端操作符

1、中间操作符

Stream API 提供了多种中间操作符,用于对流进行转换、筛选、映射等操作。下面列举一些常用的中间操作符:

  1. filter(Predicate predicate):根据给定的条件筛选元素,保留满足条件的元素。
  2. map(Function<T, R> mapper):将元素按照指定的映射规则进行转换,生成新的元素。
  3. flatMap(Function<T, Stream> mapper):将每个元素映射为一个流,并将所有流合并为单个流。
  4. distinct():去除流中的重复元素。
  5. sorted():对流中的元素进行自然排序(要求元素实现 Comparable 接口)。
  6. sorted(Comparator comparator):根据指定的比较器对流中的元素进行排序。
  7. limit(long maxSize):限制流的大小,截断流使其最多包含指定数量的元素。
  8. skip(long n):跳过流中的前 n 个元素,返回剩余的元素流。
  9. peek(Consumer action):对每个元素执行操作,类似于 forEach,但不会终止流。
  10. takeWhile(Predicate predicate):从开头开始取元素,直到遇到不满足条件的元素为止。
  11. dropWhile(Predicate predicate):从开头开始丢弃元素,直到遇到不满足条件的元素为止。
  12. parallel():将流转换为并行流,以支持并行处理。

举例:

        List<Student> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Student student = new Student();
            student.setId((1111111111+i));
            student.setName(String.valueOf(i));
            student.setAge(i);
            list.add(student);
        }
        //使用filter,筛选age大于50的对象
        List<Student> collect = list.stream().filter(student -> student.getAge() > 50).collect(Collectors.toList());

		 //使用filter以及map,将age大于50的学生姓名输出
        List<String> collect = list.stream().filter(student -> student.getAge() > 50).map(Student::getName).collect(Collectors.toList());

2、终端操作符

Stream流的终端操作执行后,该流将无法再执行其他动作。如果试图执行其他操作,会导致状态异常并提示该流已经被执行操作或者被关闭。因此为了再次执行操作,必须重新创建Stream流。

需要注意的是,一个流有且只能有一个终端操作。一旦这个终端操作执行后,流就会被关闭,无法再被操作。因此,一个流只能被遍历一次。如果需要在遍历后再次操作流,必须通过源数据重新生成流。

终端操作的执行,才会真正开始流的遍历。如 count、collect 等

  1. Collect:收集器,将流转换为其他形式
  2. forEach:对该流的每个元素执行一个操作,遍历流中的数据。
  3. findFirst:返回第一个元素
  4. findAny:返回当前流中的任意元素
  5. sum:返回此流中元素的总和。
  6. count:返回流中元素总数
  7. max:返回此流的最大元素
  8. min:返回此流的最小元素
  9. anyMatch:返回此流的任何元素是否与所提供的条件匹配。只有有一个匹配则返回ture,否则返回false。如果流为空,则返回false。
    检查是否至少匹配一个元素,返回boolean
  10. allMatch:返回此流的所有元素是否与所提供的条件匹配。如果全部匹配则返回true,否则返回false。如果流为空,则返回true。
  11. noneMatch:返回此流中是否没有元素与所提供的条件匹配。检查是否没有匹配所有元素,返回boolean。
  12. reduce:使用关联累加函数对该流的元素执行约简操作,并返回一个描述约简值的Optional(如果有的话)。也就是对流中的元素进行累计的操作

3、Collect收集器详解

//	将数据存放到List集合中
 List<String> list = people.stream()
   .map(Person::getName)
   .collect(Collectors.toList());

 // 将数据存放到Set集合中
 Set<String> set = people.stream()
   .map(Person::getName)
   .collect(Collectors.toCollection(TreeSet::new));

 // 将元素转换为字符串并连接它们,用逗号分隔
 String joined = things.stream()
   .map(Object::toString)
   .collect(Collectors.joining(", "));

 // 计算员工工资总额
 int total = employees.stream()
   .collect(Collectors.summingInt(Employee::getSalary));

 // 按部门分组员工
 Map<Department, List<Employee>> byDept = employees.stream()
   .collect(Collectors.groupingBy(Employee::getDepartment));

 // 按部门计算工资总额
 Map<Department, Integer> totalByDept = employees.stream()
   .collect(Collectors.groupingBy(Employee::getDepartment,
                                  Collectors.summingInt(Employee::getSalary)));

 // 把学生分为及格和不及格
 Map<Boolean, List<Student>> passingFailing = students.stream()
   .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

结束

Stream API 提供了一种简洁、强大和可读性高的方式来处理数据集合。它的函数式风格和操作链式调用使得代码更加简洁和易于理解。通过合理地使用 Stream API,可以实现高效、简洁和可维护的数据处理代码。

如果大家觉得文章内容不错,快去分享给更多小伙伴吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

旺仔001

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

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

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

打赏作者

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

抵扣说明:

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

余额充值