当前位置:首页 > 技术 > 正文内容

Java Stream API 深度解析与实战指南

访客 技术 2026年7月4日 1

Stream 核心概念

Java 8 引入的 Stream API 彻底改变了集合处理方式,它提供了一种声明式、函数式的数据操作范式。Stream 并非数据结构,而是对数据源的元素序列进行计算的抽象视图。

核心接口体系

接口 功能定位
Stream<T> 泛型对象流,支持顺序与并行聚合操作
IntStream 原始 int 类型特化流,避免装箱开销
LongStream 原始 long 类型特化流
DoubleStream 原始 double 类型特化流
Collector<T,A,R> 可变归约操作,用于将流元素累积到结果容器

Stream 的五大特性

  1. 无存储性:Stream 本身不保存数据,仅通过管道传输来自集合、数组、生成器或 I/O 通道的元素
  2. 不可变性:操作生成新 Stream 而非修改源数据,过滤操作不会删除原集合元素
  3. 惰性求值:中间操作延迟执行,终端操作触发实际计算,支持短路优化(如 findFirst
  4. 无限可能性:配合 limitfindFirst 等短路操作可处理无限流
  5. 单次消费:元素仅能被访问一次,需重新创建 Stream 以重复遍历

Stream 创建方式详解

从集合构建

// 顺序流与并行流
List<String> words = Arrays.asList("alpha", "beta", "gamma");
Stream<String> sequential = words.stream();
Stream<String> parallel = words.parallelStream();

从数组构建

int[] scores = {85, 92, 78, 95};
IntStream scoreStream = Arrays.stream(scores);

String[] names = {"Alice", "Bob", "Carol"};
Stream<String> nameStream = Arrays.stream(names, 1, 3); // 指定范围

使用 Stream 工厂方法

// 固定元素流
Stream<String> colors = Stream.of("red", "green", "blue");

// 无限迭代流
Stream<BigInteger> powersOfTwo = Stream.iterate(
    BigInteger.ONE, 
    n -> n.shiftLeft(1)
).limit(10);

// 无限生成流
Stream<UUID> randomIds = Stream.generate(UUID::randomUUID).limit(5);

其他数据源转换

// 正则分割
Pattern delimiter = Pattern.compile("\\s+");
Stream<String> tokens = delimiter.splitAsStream("hello world stream api");

// 文件行流
try (BufferedReader br = Files.newBufferedReader(Paths.get("data.txt"))) {
    Stream<String> lines = br.lines();
    // 处理逻辑...
}

核心操作实战

映射转换(map / flatMap)

class Product {
    String name;
    double price;
    // 构造方法、getter 省略
}

List<Product> inventory = Arrays.asList(
    new Product("Laptop", 8999.00),
    new Product("Phone", 4999.00),
    new Product("Tablet", 2999.00)
);

// 提取价格并计算折扣
List<Double> discountedPrices = inventory.stream()
    .map(p -> p.getPrice() * 0.9)
    .collect(Collectors.toList());

// flatMap 展平嵌套结构
List<List<Integer>> matrix = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5, 6),
    Arrays.asList(7, 8, 9)
);

List<Integer> flattened = matrix.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList()); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

过滤与匹配

List<Integer> values = Arrays.asList(10, 25, 30, 45, 50, 65);

// 多条件过滤
List<Integer> result = values.stream()
    .filter(v -> v > 20)
    .filter(v -> v % 10 == 5)
    .collect(Collectors.toList()); // [25, 45, 65]

// 短路匹配
boolean hasEven = values.stream().anyMatch(v -> v % 2 == 0);
boolean allPositive = values.stream().allMatch(v -> v > 0);
boolean noNegative = values.stream().noneMatch(v -> v < 0);

排序与截断

List<String> cities = Arrays.asList("Shenzhen", "Beijing", "Shanghai", "Guangzhou");

// 自然排序与自定义比较器
List<String> byLength = cities.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());

// 分页实现
int pageSize = 2;
int pageNum = 2; // 第2页

List<String> pageData = cities.stream()
    .skip((pageNum - 1) * pageSize)
    .limit(pageSize)
    .collect(Collectors.toList()); // [Shanghai, Guangzhou]

归约与聚合

List<BigDecimal> amounts = Arrays.asList(
    new BigDecimal("100.50"),
    new BigDecimal("250.75"),
    new BigDecimal("80.25")
);

// 求和
BigDecimal total = amounts.stream()
    .reduce(BigDecimal.ZERO, BigDecimal::add);

// 分组统计
Map<String, Long> categoryCount = inventory.stream()
    .collect(Collectors.groupingBy(
        Product::getCategory, 
        Collectors.counting()
    ));

// 分区
Map<Boolean, List<Product>> pricePartition = inventory.stream()
    .collect(Collectors.partitioningBy(p -> p.getPrice() > 5000));

调试与副作用操作

// peek 用于调试,观察中间状态
List<Integer> processed = Stream.of(1, 2, 3, 4, 5)
    .peek(n -> System.out.println("原始值: " + n))
    .map(n -> n * n)
    .peek(n -> System.out.println("平方后: " + n))
    .filter(n -> n > 10)
    .collect(Collectors.toList());

方法速查表

静态构造方法

方法 说明
Stream.of(T... values) 由指定元素创建顺序流
Stream.iterate(T seed, UnaryOperator<T> f) 迭代生成无限有序流
Stream.generate(Supplier<T> s) 生成无限无序流
Stream.concat(Stream<T> a, Stream<T> b) 延迟连接两个流
Stream.empty() 创建空流

中间操作

方法 功能
filter(Predicate<T>) 条件过滤
map(Function<T,R>) 一对一映射
flatMap(Function<T,Stream<R>>) 一对多展平映射
distinct() 去重(基于 equals)
sorted() / sorted(Comparator) 排序
peek(Consumer<T>) 检视元素
limit(long) 截断保留前 n 个
skip(long) 跳过前 n 个

终端操作

方法 功能
forEach(Consumer<T>) 遍历消费
toArray() 转为数组
reduce(BinaryOperator<T>) 归约聚合
collect(Collector<T,A,R>) 收集到容器
min/max(Comparator<T>) 最值
count() 计数
anyMatch/allMatch/noneMatch 匹配判断
findFirst/findAny 查找元素

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。