Java Stream API

一、Java Stream 概述

Java 8 引入的 Stream API 提供了一种高效且简洁的方式来处理集合数据。Stream 允许你以声明式方式处理数据集合(如 List、Set 等),并支持过滤、映射、排序、聚合等操作,使代码更易读且更高效。

核心特点:

  • 不存储数据:Stream 不存储元素,而是对数据源(如集合)进行计算操作。
  • 函数式编程:Stream 操作不会修改原数据,而是返回新的 Stream。
  • 延迟执行:中间操作(如 filtermap)返回 Stream 本身,直到终端操作(如 collectforEach)才会触发实际计算。
  • 可并行处理:通过 parallelStream() 轻松实现并行操作,提升大数据量处理性能。

二、Stream 的基本操作流程

  1. 创建 Stream:从集合、数组等数据源获取 Stream。
  2. 中间操作:对数据进行过滤、映射等处理,返回新的 Stream。
  3. 终端操作:触发实际计算,返回结果或副作用(如打印、收集到集合)。

三、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. 从原始类型数组创建流

专门针对 intlongdouble 的优化流。

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.streamjava.io(传统 IO)、java.nio(NIO)
核心接口StreamIntStreamDoubleStreamInputStreamOutputStreamReaderWriter
关键类CollectorsStreamSupportFileInputStreamBufferedWriterFileChannel

总结

  • Stream 流:是 内存中的数据处理工具,用于高效操作集合元素,强调数据计算逻辑(如过滤、映射)。
  • IO 流:是 设备间的数据传输通道,用于读写文件、网络等外部数据,强调数据输入输出操作(如读取文件内容、发送网络请求)。

两者虽然都被称为“流”,但设计目标和使用场景完全不同,需根据具体需求选择合适的工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值