一、Java Stream 概述
Java 8 引入的 Stream API 提供了一种高效且简洁的方式来处理集合数据。Stream 允许你以声明式方式处理数据集合(如 List、Set 等),并支持过滤、映射、排序、聚合等操作,使代码更易读且更高效。
核心特点:
- 不存储数据:Stream 不存储元素,而是对数据源(如集合)进行计算操作。
- 函数式编程:Stream 操作不会修改原数据,而是返回新的 Stream。
- 延迟执行:中间操作(如
filter
、map
)返回 Stream 本身,直到终端操作(如collect
、forEach
)才会触发实际计算。 - 可并行处理:通过
parallelStream()
轻松实现并行操作,提升大数据量处理性能。
二、Stream 的基本操作流程
- 创建 Stream:从集合、数组等数据源获取 Stream。
- 中间操作:对数据进行过滤、映射等处理,返回新的 Stream。
- 终端操作:触发实际计算,返回结果或副作用(如打印、收集到集合)。
三、Stream 的创建方式
在 Java Stream API 中,创建流(Stream)是使用流操作的第一步。Java 提供了多种创建流的方式,适用于不同的数据源和场景。以下是常见的创建流的方法:
1. 从集合创建流
通过 Collection
接口的 stream()
或 parallelStream()
方法创建流。
import java.util.Arrays;
import java.util.List;
public class CollectionStream {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 顺序流
names.stream()
.forEach(System.out::println);
// 并行流
names.parallelStream()
.forEach(System.out::println);
}
}
2. 从数组创建流
使用 Arrays.stream()
或 Stream.of()
方法。
import java.util.stream.Stream;
public class ArrayStream {
public static void main(String[] args) {
String[] fruits = {"apple", "banana", "cherry"};
// 方式一:Arrays.stream()
Stream<String> stream1 = Arrays.stream(fruits);
// 方式二:Stream.of()
Stream<String> stream2 = Stream.of(fruits);
// 直接传入多个元素
Stream<Integer> numbers = Stream.of(1, 2, 3, 4);
}
}
3. 创建空流
使用 Stream.empty()
创建一个空的流。
import java.util.stream.Stream;
public class EmptyStream {
public static void main(String[] args) {
Stream<String> emptyStream = Stream.empty();
System.out.println(emptyStream.count()); // 输出 0
}
}
4. 使用 Stream.builder()
通过构建器动态添加元素。
import java.util.stream.Stream;
public class BuilderStream {
public static void main(String[] args) {
Stream<String> stream = Stream.<String>builder()
.add("apple")
.add("banana")
.add("cherry")
.build();
stream.forEach(System.out::println);
}
}
5. 创建无限流
5.1 迭代流(iterate)
import java.util.stream.Stream;
public class IterateStream {
public static void main(String[] args) {
// 生成偶数序列:0, 2, 4, 6, 8
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);
// 注意:无限流必须配合终止操作(如 limit)
evenNumbers.limit(5)
.forEach(System.out::println);
}
}
5.2 生成流(generate)
import java.util.stream.Stream;
public class GenerateStream {
public static void main(String[] args) {
// 生成随机数
Stream<Double> randomNumbers = Stream.generate(Math::random);
// 取前3个随机数
randomNumbers.limit(3)
.forEach(System.out::println);
}
}
6. 从原始类型数组创建流
专门针对 int
、long
、double
的优化流。
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.DoubleStream;
public class PrimitiveStream {
public static void main(String[] args) {
// 创建 IntStream
IntStream intStream = IntStream.of(1, 2, 3, 4);
// 创建范围流(1 到 10,包含1,不包含10)
IntStream range = IntStream.range(1, 10);
// 创建范围流(1 到 10,包含1和10)
LongStream rangeClosed = LongStream.rangeClosed(1, 10);
// 创建 DoubleStream
DoubleStream doubleStream = DoubleStream.generate(() -> Math.PI);
}
}
7. 从文件创建流
读取文件的每一行作为流的元素。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class FileStream {
public static void main(String[] args) {
try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
8. 从函数创建流
使用 Stream.generate()
或 Stream.iterate()
结合函数式接口。
import java.util.stream.Stream;
public class FunctionStream {
public static void main(String[] args) {
// 生成常量流
Stream<String> constantStream = Stream.generate(() -> "Hello");
// 斐波那契数列
Stream<int[]> fibonacci = Stream.iterate(
new int[]{0, 1},
arr -> new int[]{arr[1], arr[0] + arr[1]}
);
fibonacci.limit(5)
.map(arr -> arr[0])
.forEach(System.out::println); // 输出: 0, 1, 1, 2, 3
}
}
9. 合并多个流
使用 Stream.concat()
合并两个流。
import java.util.stream.Stream;
public class ConcatStream {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("apple", "banana");
Stream<String> stream2 = Stream.of("cherry", "date");
Stream<String> combinedStream = Stream.concat(stream1, stream2);
combinedStream.forEach(System.out::println);
}
}
总结
创建方式 | 示例代码 | 适用场景 |
---|---|---|
集合.stream() | list.stream() | 从集合创建顺序流 |
集合.parallelStream() | list.parallelStream() | 从集合创建并行流 |
Arrays.stream() | Arrays.stream(array) | 从数组创建流 |
Stream.of() | Stream.of(1, 2, 3) | 直接创建元素流 |
Stream.empty() | Stream.empty() | 创建空流 |
Stream.builder() | Stream.builder().add("a").build() | 动态构建流 |
Stream.iterate() | Stream.iterate(0, n -> n + 2) | 创建无限迭代流 |
Stream.generate() | Stream.generate(Math::random) | 创建无限生成流 |
Files.lines() | Files.lines(Paths.get("file.txt")) | 从文件创建流 |
Stream.concat() | Stream.concat(stream1, stream2) | 合并多个流 |
根据不同的数据源和需求,选择合适的创建方式可以让流操作更加高效和简洁。
四、常用中间操作
在 Java Stream API 中,中间操作(Intermediate Operations) 是对流进行转换、过滤或映射的操作,它们返回一个新的 Stream,允许链式调用多个中间操作。中间操作具有延迟执行的特性,即只有在终端操作被调用时才会真正执行计算。
1. 过滤(Filter)
筛选符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.collect(Collectors.toList()); // [2, 4, 6]
2. 映射(Map)
将元素转换为另一种类型。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
.map(String::length) // 映射为字符串长度
.collect(Collectors.toList()); // [5, 3, 7]
3. 扁平化(FlatMap)
将嵌套的集合展开为一个流。
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4)
);
List<Integer> flattenedList = nestedList.stream()
.flatMap(Collection::stream) // 将每个子列表展开为单个元素
.collect(Collectors.toList()); // [1, 2, 3, 4]
4. 排序(Sorted)
对流元素进行排序。
List<String> fruits = Arrays.asList("banana", "apple", "cherry");
List<String> sortedFruits = fruits.stream()
.sorted() // 自然排序(字典序)
.collect(Collectors.toList()); // [apple, banana, cherry]
// 自定义排序
List<Integer> unsortedNumbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List<Integer> sortedNumbers = unsortedNumbers.stream()
.sorted(Comparator.reverseOrder()) // 降序排序
.collect(Collectors.toList()); // [9, 5, 4, 3, 1, 1]
5. 去重(Distinct)
去除重复元素。
List<Integer> numbersWithDuplicates = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> distinctNumbers = numbersWithDuplicates.stream()
.distinct() // 去重
.collect(Collectors.toList()); // [1, 2, 3]
6. 截断(Limit)与跳过(Skip)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> firstThree = numbers.stream()
.limit(3) // 取前3个元素
.collect(Collectors.toList()); // [1, 2, 3]
List<Integer> skipTwo = numbers.stream()
.skip(2) // 跳过前2个元素
.collect(Collectors.toList()); // [3, 4, 5]
五、常用终端操作
在 Java Stream API 中,终端操作(Terminal Operation) 是触发流的实际计算并产生最终结果的操作。终端操作执行后,流会被消费掉,无法再次使用。
1. 收集(Collect)
将流元素收集到集合或其他数据结构中。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 收集到 List
List<String> resultList = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
// 收集到 Set(自动去重)
Set<String> resultSet = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toSet());
// 收集到 Map(需要指定键和值的映射)
Map<String, Integer> nameLengthMap = names.stream()
.collect(Collectors.toMap(
name -> name, // 键:名字本身
name -> name.length() // 值:名字长度
));
2. 聚合(Reduce)
将流中的元素合并为单个结果。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream()
.reduce(0, Integer::sum); // 初始值为0,累加所有元素,结果:15
// 求最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max); // 结果:Optional[5]
3. 匹配(Match)
检查流中的元素是否满足条件。
List<Integer> numbers = Arrays.asList(2, 4, 6, 8);
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0); // 所有元素都是偶数?true
boolean anyGreaterThanFive = numbers.stream()
.anyMatch(n -> n > 5); // 存在大于5的元素?true
boolean noneNegative = numbers.stream()
.noneMatch(n -> n < 0); // 没有负数?true
4. 查找(Find)
查找流中的元素。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 查找第一个元素
Optional<String> first = names.stream()
.filter(name -> name.startsWith("C"))
.findFirst(); // Optional[Charlie]
// 查找任意元素(适用于并行流)
Optional<String> any = names.parallelStream()
.filter(name -> name.length() > 3)
.findAny(); // 可能返回任意符合条件的元素
5. 遍历(ForEach)
对每个元素执行操作。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println); // 输出:ALICE, BOB, CHARLIE
6. 统计(Count)
计算流中元素的数量。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
.filter(n -> n > 3)
.count(); // 符合条件的元素数量:2
六、并行流(Parallel Stream)
在 Java Stream API 中,并行流(Parallel Stream) 是一种能够利用多核处理器并行执行的流。它通过将数据分成多个片段并在不同线程中同时处理,显著提升大数据量的处理性能。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 顺序流处理(单线程)
long sumSequential = numbers.stream()
.reduce(0, Integer::sum);
// 并行流处理(多线程)
long sumParallel = numbers.parallelStream()
.reduce(0, Integer::sum);
// 注意:并行流适用于大数据量和计算密集型操作,小数据量可能反而更慢
七、与集合的区别
特性 | 集合(Collection) | Stream |
---|---|---|
存储数据 | 存储元素 | 不存储元素,只对数据进行计算 |
修改原数据 | 操作会修改原集合 | 操作不会修改原数据,返回新的 Stream |
迭代方式 | 外部迭代(手动遍历) | 内部迭代(自动处理) |
执行时机 | 立即执行 | 延迟执行(终端操作触发) |
可复用性 | 可多次使用 | 只能使用一次 |
八、与IO流的区别
Java 中的 Stream 流(Java 8 引入的 Stream API)和 IO 流(Input/Output Stream,Java IO 体系)是完全不同的概念,两者的设计目标、功能和应用场景差异显著。以下是它们的核心区别对比:
1.设计目标与核心功能
特性 | Stream 流(Java Stream API) | IO 流(Java IO/NIO) |
---|---|---|
本质 | 用于内存中集合数据的处理,是函数式编程的工具 | 用于设备间的数据传输(如文件、网络、内存等) |
核心功能 | 过滤、映射、聚合、排序等数据操作,注重数据计算 | 读取/写入数据,注重数据的输入输出操作 |
数据流向 | 单向(从数据源到终端操作),无输入输出概念 | 双向(输入流/输出流),明确数据读写方向 |
处理方式 | 声明式(通过方法链描述操作) | 命令式(通过循环和条件语句手动控制读写) |
2.数据存储与操作
特性 | Stream 流 | IO 流 |
---|---|---|
数据源 | 集合(List/Set)、数组、文件行(通过 Files.lines ) | 文件、网络连接、内存缓冲区、控制台等 |
数据存储 | 不存储数据,操作内存中的现有数据 | 数据可能存储在磁盘、网络或其他外部设备中 |
操作性质 | 无状态(中间操作不修改原数据,返回新流) | 有状态(读写操作会改变流的位置或状态) |
典型操作 | filter() 、map() 、collect() 、reduce() | read() 、write() 、flush() 、close() |
3.执行机制与生命周期
特性 | Stream 流 | IO 流 |
---|---|---|
执行时机 | 延迟执行(中间操作等待终端操作触发) | 立即执行(读写操作实时生效) |
流的关闭 | 无需手动关闭(终端操作后自动释放资源) | 需要手动关闭(避免资源泄漏,如 try-with-resources ) |
复用性 | 只能使用一次(终端操作后流被消费) | 部分流可复用(如缓冲流重置标记) |
并行处理 | 支持并行流(parallelStream() ) | 通常为单线程操作(需手动实现多线程读写) |
4.典型应用场景
特性 | Stream 流 | IO 流 |
---|---|---|
常见场景 | - 数据清洗与转换 - 集合元素过滤与统计 - 大数据量并行计算 | - 文件读写(如文本、二进制文件) - 网络数据传输 - 控制台输入输出 |
示例代码 | java<br>list.stream().filter(n -> n>0).sum();<br> | java<br>FileInputStream fis = new FileInputStream("data.txt");<br> |
性能优化 | 通过并行流和惰性计算提升大数据处理效率 | 通过缓冲流(BufferedInputStream )减少 IO 次数 |
5.核心类库与层级
特性 | Stream 流 | IO 流 |
---|---|---|
所属包 | java.util.stream | java.io (传统 IO)、java.nio (NIO) |
核心接口 | Stream 、IntStream 、DoubleStream | InputStream 、OutputStream 、Reader 、Writer |
关键类 | Collectors 、StreamSupport | FileInputStream 、BufferedWriter 、FileChannel |
总结
- Stream 流:是 内存中的数据处理工具,用于高效操作集合元素,强调数据计算逻辑(如过滤、映射)。
- IO 流:是 设备间的数据传输通道,用于读写文件、网络等外部数据,强调数据输入输出操作(如读取文件内容、发送网络请求)。
两者虽然都被称为“流”,但设计目标和使用场景完全不同,需根据具体需求选择合适的工具。