Java8 Stream API 详解:流式编程进行数据处理

简介: Java8 Stream API 详解:流式编程进行数据处理


前言


Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。


Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式


流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”


注意:


① Stream 自己不会存储元素。


② Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。


③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream API的三个阶段


在Java中,Stream 是Java 8引入的一个新概念,用于处理集合(Collections)数据的一种抽象。Java的Stream API 提供了一种声明式的方式来操作数据集合,可以用更简洁、可读性更强的代码来进行集合的操作。


Java Stream API的操作可以分为三个阶段:


1. 创建流(Creation of Stream): 这个阶段涉及到从不同的数据源创建流,可以是集合、数组、I/O通道等。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();


2. 中间操作(Intermediate Operations): 这个阶段包括对流的转换操作,可以对流进行过滤、映射、排序等操作。这些操作并不会改变原始数据源,而是返回一个新的流。

Stream<Integer> filteredStream = stream.filter(x -> x > 2);


3. 终端操作(Terminal Operations): 这个阶段是对流进行最终操作,触发流的遍历,可以产生一个结果或者副作用。终端操作是流的最后一个操作,执行后流将不可再用。

long count = filteredStream.count();

这三个阶段的设计使得可以通过链式调用的方式组合多个操作,从而编写更为清晰和简洁的代码。这种方式也有助于提高代码的可读性和可维护性。


当然,这里只是对于Stream API三个阶段的概述,只是告诉大家,简单分为三个阶段,至于三个阶段里面有哪些主要的方法,我们在下文进行详细叙述,这里我们点到为止!现在,大家心里面就应该有这么一个蓝图,或者是基本框架,知道我们接下来将会沿着那个几个方向展开叙述!

创建Stream流

在Java中,你可以使用多种方式来创建Stream流。

从集合创建:

使用集合类的 stream() 或 parallelStream() 方法可以创建对应的流。例如:

List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> streamFromList = list.stream();

从数组创建:

使用 Arrays.stream() 方法可以从数组中创建流:

String[] array = {"apple", "banana", "orange"};
Stream<String> streamFromArray = Arrays.stream(array);

通过Stream的静态方法创建:

Stream 类提供了静态方法 of(),可以传入一系列元素来创建流:

Stream<String> stream = Stream.of("apple", "banana", "orange");

使用Stream的generate和iterate方法:

Stream 类还提供了 generate 和 iterate 方法,用于生成无限流:

// 生成包含随机整数的无限流
Stream<Integer> infiniteStream = Stream.generate(() -> (int) (Math.random() * 100));
 
// 从指定的起始值开始,按照某个规则生成无限流
Stream<Integer> sequentialStream = Stream.iterate(1, n -> n + 1);

通过文件生成流:

java.nio.file.Files 类提供了静态方法 lines(),可以用来读取文件内容并生成流:

Path path = Paths.get("example.txt");
Stream<String> fileLines = Files.lines(path);


使用正则表达式生成流:

Pattern 类的 splitAsStream 方法可以根据正则表达式将字符串分割成流:

String text = "apple,orange,banana";
Stream<String> textStream = Pattern.compile(",").splitAsStream(text);

Stream API中间操作

Stream API 提供了许多中间操作,用于对流进行转换、筛选和处理。


filter

用于筛选元素,根据指定的条件保留符合条件的元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> filteredStream = numbers.stream().filter(x -> x > 2);


map

对流中的每个元素应用指定的函数,并将结果映射为一个新的元素。

List<String> words = Arrays.asList("apple", "banana", "orange");
Stream<Integer> wordLengths = words.stream().map(String::length);


flatMap

将流中的每个元素都转换为一个流,然后将这些流连接起来成为一个流。

List<List<Integer>> numbers = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
 
Stream<Integer> flatStream = numbers.stream().flatMap(List::stream);

distinct

去除流中的重复元素。

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
Stream<Integer> distinctNumbers = numbers.stream().distinct();

sorted

对流中的元素进行排序。

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
Stream<Integer> sortedNumbers = numbers.stream().sorted();

peek

对流中的每个元素执行操作,主要用于调试和观察流中的元素。

List<String> words = Arrays.asList("apple", "banana", "orange");
Stream<String> peekStream = words.stream().peek(System.out::println);


limit 和 skip

limit 用于截断流,保留指定数量的元素,而 skip 则用于跳过指定数量的元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> limitedStream = numbers.stream().limit(3);
Stream<Integer> skippedStream = numbers.stream().skip(2);

Stream API终端操作

Stream API 的终端操作用于触发对流的最终操作,产生结果或者引起副作用。

forEach

对流中的每个元素执行指定的操作。

List<String> words = Arrays.asList("apple", "banana", "orange");
words.stream().forEach(System.out::println);

toArray

将流中的元素转换为数组。

List<String> words = Arrays.asList("apple", "banana", "orange");
String[] wordArray = words.stream().toArray(String[]::new);

reduce

对流中的元素进行归约操作,可以用于求和、求最大值、最小值等。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = numbers.stream().reduce(Integer::sum);

collect

将流中的元素收集到一个集合中,例如 List、Set 或 Map。

List<String> words = Arrays.asList("apple", "banana", "orange");
List<String> collectedWords = words.stream().collect(Collectors.toList());


count

返回流中的元素数量。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();

anyMatch、allMatch 和 noneMatch

用于检查流中是否存在满足指定条件的元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> x > 3);
boolean allGreaterThanTwo = numbers.stream().allMatch(x -> x > 2);
boolean noneGreaterThanFive = numbers.stream().noneMatch(x -> x > 5);


findAny 和 findFirst

返回流中的任意一个元素或者第一个元素。

List<String> words = Arrays.asList("apple", "banana", "orange");
Optional<String> anyWord = words.stream().findAny();
Optional<String> firstWord = words.stream().findFirst();

min 和 max

返回流中的最小值或最大值。

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6);
Optional<Integer> minNumber = numbers.stream().min(Integer::compare);
Optional<Integer> maxNumber = numbers.stream().max(Integer::compare);

注意事项

使用Stream API时,有一些需要注意的重要事项,以确保正确、高效地利用这一功能:


只能使用一次: 一个 Stream 实例只能被消费(执行终端操作)一次。如果你尝试对已经使用过的流进行其他终端操作,会抛出 IllegalStateException 异常。如果需要再次操作,可以重新创建一个新的流。

List<String> words = Arrays.asList("apple", "banana", "orange");
Stream<String> wordStream = words.stream();
 
// 正确的做法
long count = wordStream.count();
 
// 错误的做法,会抛出IllegalStateException
long anotherCount = wordStream.count();

及早退出: 在处理大量数据时,及早退出可以提高性能。使用 anyMatch()、findFirst() 等终端操作时,一旦找到符合条件的元素,就会立即返回,不再继续处理后续元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> {
    System.out.println("Checking: " + x);
    return x > 3;
});


并行流的谨慎使用: Stream API 提供了并行流的支持,可以通过 parallel() 方法将顺序流转换为并行流。但并不是所有的场景都适合使用并行流,因为在某些情况下,并行流可能会导致性能下降,甚至出现并发问题。在并行流的使用上需要注意线程安全等问题。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.parallelStream().filter(x -> x > 2).count();

使用适当的数据结构: 在创建流时,选择适当的数据结构能够影响流操作的性能。例如,ArrayList 在顺序访问时性能较好,而 LinkedList 在随机访问时性能较好。

总结

总体而言,了解Stream API的使用原则,结合具体的业务场景和性能需求,能够更好地利用Stream API完成任务。注意流的延迟计算特性,避免副作用,可以使代码更加清晰、可读,并提高代码的可维护性。


相关文章
|
1月前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
57 6
|
1月前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
2月前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
28 0
|
运维 监控 Java
Java版阿里云通信短信发送API接口实例(1)
Java版阿里云通信短信发送API接口实例(新)
1135 0
Java版阿里云通信短信发送API接口实例(1)
|
JSON Java API
Java版阿里云通信短信发送API接口实例(2)
Java版阿里云通信短信发送API接口实例(新)
1099 0
|
Java API 开发工具
Java版阿里云通信短信发送API接口实例(新)
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/78751698 阿里云通信(原名阿里大于)的短信服务(Short Message Service)是阿里云为用户提供的一种通信服务的能力,支持快速发送短信验证码、短信通知等。
3577 1
|
10天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
12天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
12天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
13天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3