Java 8中stream流处理(入门教程)

简介: 本文中流处理是指对运动中的数据的处理,在生成或接收数据直接计算数据。应用程序中分析和查询不断存在,数据不断地流经它们。在从流中接收到事件时,流处理应用程序对该事件作出反应。

基础概念

“流”是一个抽象的概念,它是对输入输出设备的一种抽象理解,在java中,对数据的输入输出操作都是以“流”的方式进行的。“流”具有方向性,输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流,相反,写出数据到某个数据源目的地的时候也会开启一个输出流。数据源可以是文件、内存或者网络等。

本文中流处理是指对运动中的数据的处理,在生成或接收数据直接计算数据。应用程序中分析和查询不断存在,数据不断地流经它们。在从流中接收到事件时,流处理应用程序对该事件作出反应。

如果我们使用传统的循环迭代方式对数据集进行复杂计算,常常会带来两个弊端:

  1. 迭代次数多,迭代次数跟函数调用的次数相等。
  2. 频繁产生中间结果,存储开销无法接受。

流处理可以立即对事件做出反应,且可以处理比其他数据处理系统大得多的数据量:直接处理事件流,并且只保留数据中有意义的子集。尤其是面对持续生成,本质上是无穷尽的数据集。

Java Stream 类

JDK 1.8 新增。将要处理的元素集合看作一种流,在管道的节点上进行处理。使代码更简洁易读。

集合接口有两个方法来生成流,数据类型将由 Collection 转化为 Stream 。

  • stream() 方法:为集合创建串行流。
  • parallelStream() 方法:为集合创建并行流。
  1. Stream 的遍历方式和结果与 Iterator 无差别(便于转化),其优势在于其原型链的设计使得它可以对遍历处理后的数据进行再处理。
  2. parallelStream 提供了流的并行处理,底层使用 Fork/Join 框架,简单理解就是多线程异步任务的一种实现。处理过程中会有多个线程处理元素,具体由 JDK 负责管理。不保证有序性。
  3. 串行流和并行流之间可以通过 parallel 和 sequential 方法相互转化。
Stream<Integer> stream = list.stream();                     // 声明作为流处理
ParellerStream<Integer> pStream = stream.parallel();        // 转化为并行流

流操作

流处理的每个操作阶段都会封装到一个 Sink 接口里,处理数据后再将数据传递给下游的 Sink。
Stream 上的所有操作分为两类:中间操作和结束操作。
Stream 是延迟执行的,只有调用到结束操作,才触发整个流水线的执行。如果未定义结束操作,那么流处理什么也不会做。

流的初体验
long count = strings.stream()                   //声明作为流处理
                .filter(e -> e.isEmpty())      //中间操作,过滤空元素
                .count();                     //结束操作,计算

中间操作

过滤filter

==filter()方法用于通过设置的条件过滤出元素==

 List<String> strings =Arrays.asList("aa", "bb", "cc", "", "");
        long count = strings.stream()           //声明作为流处理
                .filter(e -> e.isEmpty())      //中间操作,过滤空元素
                .count();                      //结束操作,计算
        System.out.println(count);

筛选limit/skip

==limit()方法用于获取指定数量的流(前n个),skip()方法用于去除指定数量的流(前n个)==

Random random = new Random();
        random.ints()
                .limit(10)
                .skip(5)
                .forEach(value -> System.out.println(value));

映射map

==map()方法用于映射每个元素到对应的结果,其实就是对结果进行转化==

List<Integer> integers = Arrays.asList(3, 2, 5, 4, 6, 8, 9, 1);
        List<Integer> list = integers.stream()
                                     .map(i -> i * i)
                                     .collect(Collectors.toList());
        System.out.println(list.toString());

排序sorted

==sorted()方法通过Comparable接口对流进行排序,也可以自定义==

 Random random = new Random();
        random.ints().limit(10)
                     .sorted()
                     .forEach(System.out::println);

使用Comparator 来排序一个list
说明:统计学生毕业后薪资,将同学按年龄由大到小进行排序

 ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("zx",28,8000));
        students.add(new Student("zr",29,8500));
        students.add(new Student("zt",25,8000));
        students.add(new Student("zy",35,10000));
        students.add(new Student("zu",22,6000));
        
        //默认升序排列,使用reversed()达到降序效果
        students.stream().sorted(Comparator.comparing(Student::getAge).reversed()) 
                         .forEach(System.out::println);

通过Comparator.thenComparing(Comparator<? super T> other) 实现多字段排序
说明:统计学生毕业后薪资,将同学按年龄由小到大进行排序,年龄相等时,按薪资升序排序

 ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("zx",25,8000));
        students.add(new Student("zr",25,8500));
        students.add(new Student("zt",25,8000));
        students.add(new Student("zy",35,10000));
        students.add(new Student("zu",22,6000));

        students.stream().sorted(Comparator.comparing(Student::getAge).thenComparing(Student::getSalary))
                         .forEach(System.out::println);

Student类的代码

public class Student {
    private String name;
    private int age;
    private int salary;

    public Student() {
    }

    public Student(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    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 int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

去重distinct

==distinct()方法通过流元素的hashCode和equals方法去除重复元素==
说明:录入学生毕业后薪资时,确保无重复数据录入

 ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("zx",25,8000));
        students.add(new Student("zx",25,8000));
        students.add(new Student("zt",35,10000));
        students.add(new Student("zt",35,10000));
        students.add(new Student("zu",22,6000));

        students.stream().distinct()
                         .forEach(System.out::println);

在Student类中增加hashCode()和equals()方法

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                salary == student.salary &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, salary);
    }

结束操作

迭代 forEach

==forEach 迭代流中的每个数据,即对每个数据进行最后的处理(比如保存到数据库中或打印)==

// 输出 10 个随机数 
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

聚合Collectors

==Collectors 类实现了归约操作,例如将流转换成集合和聚合元素,可用于返回列表或字符串==
Stream转化为List

 List<String> strings = Arrays.asList("aa", "bb", "", "cc", "dd", "ee", "", "ff");
        List<String> res = strings.stream()
                                  .filter(string -> !string.isEmpty())
                                  .collect(Collectors.toList());
        System.out.println(res);

Stream转化为Set

 List<String> strings = Arrays.asList("aa", "bb", "", "bb", "dd", "dd", "", "aa");
        Set<String> res = strings.stream()
                                  .filter(string -> !string.isEmpty())
                                  .collect(Collectors.toSet());
        System.out.println(res);

Stream转化为String

joining参数说明:

  1. 第一个参数(delimiter):在每一个元素后追加的元素
  2. 第二个参数(prefix):在转化后的整个字符串首部追加的元素
  3. 第三个参数(suffix):在转化后的整个字符串尾部追加的元素
List<String> strings = Arrays.asList("aa", "bb", "bb");
        String collect = strings.stream()
                                .filter(string -> !string.isEmpty())
                                .collect(Collectors.joining("qq","cc","oo"));
        System.out.println(collect);

输出结果为:ccaaqqbbqqbboo

统计SummaryStatistics

==收集最终产生的统计结果,它们主要用于int,double,long等基本类型上==

 List<Integer> integerList = Arrays.asList(3, 2, 3, 4, 7, 9);
        IntSummaryStatistics statistics = integerList.stream()
                                                     .mapToInt((x) -> x)
                                                     .summaryStatistics();
        System.out.println("列表中最大的数 : " + statistics.getMax());
        System.out.println("列表中最小的数 : " + statistics.getMin());
        System.out.println("所有数之和 : " + statistics.getSum());
        System.out.println("平均数 : " + statistics.getAverage());

结果:
在这里插入图片描述

目录
相关文章
|
14天前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
44 0
|
12天前
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。
|
10天前
|
Java
盘点java8 stream中隐藏的函数式接口
`shigen`是一位坚持更新文章的博客作者,记录成长历程,分享认知见解,留住感动瞬间。本文介绍了函数式接口的概念及其在Java中的应用,包括`Comparator`、`Runnable`、`Callable`等常见接口,并详细讲解了`Function`、`Predicate`、`Consumer`、`Supplier`和`Comparator`等函数式接口的使用方法及应用场景,展示了如何利用这些接口简化代码并提高编程效率。**个人IP:shigen**,与shigen一起,每天进步一点点!
24 0
盘点java8 stream中隐藏的函数式接口
|
24天前
|
分布式计算 Java API
Java 8带来了流处理与函数式编程等新特性,极大提升了开发效率
Java 8带来了流处理与函数式编程等新特性,极大提升了开发效率。流处理采用声明式编程模型,通过filter、map等操作简化数据集处理,提高代码可读性。Lambda表达式支持轻量级函数定义,配合Predicate、Function等接口,使函数式编程无缝融入Java。此外,Optional类及新日期时间API等增强功能,让开发者能更优雅地处理潜在错误,编写出更健壮的应用程序。
24 1
|
26天前
|
Java API 开发者
|
16天前
|
Java API 网络安全
探索Java中的Stream API:从基础到高级应用云计算与网络安全:技术融合与挑战
【8月更文挑战第27天】在Java的海洋中,Stream API犹如一艘强大的船,让开发者能以声明式的方式处理集合数据。本文将启航,先带你了解Stream的基本概念和用法,再深入探讨其高级特性,如并行流、管道操作以及性能考量。我们将通过具体代码示例,展示如何高效利用Stream API简化数据处理流程,提升代码的可读性和性能。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往更优雅编程风格的大门。
|
22天前
|
数据可视化 IDE Java
Java8的Stream流太难用了?看看JDFrame如何简化开发
【8月更文挑战第21天】在Java的世界里,Java 8引入的Stream API无疑是一场革命,它极大地提升了集合处理的表达能力和简洁性。然而,对于许多开发者而言,尤其是那些刚从旧版本Java迁移过来的开发者,Stream API的复杂性和抽象性可能会让人感到困惑和挫败。今天,我们就来探讨如何通过JDFrame这样的框架或工具,来简化Java 8 Stream的使用,提升开发效率。
30 0
|
25天前
|
并行计算 Java API
|
28天前
|
前端开发 Oracle Java
Java 22 新增利器: 使用 Java Stream Gather 优雅地处理流中的状态
本文我们分析了 什么 是 “流”,对比了 Java 上几种常见的 “流”库,引入和详细介绍了 Java 22 中的 Stream Gather API 。同时也简单分享了利用 虚拟线程 如何简化 StreammapConcurrent操作符的实现。