吉森的技术小站 吉森的技术小站
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

吉森

Fuel your ambition
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

    • 就从Java8开始吧(一)lambda表达式详解
    • 就从java8开始吧(二)lambda表达式和方法引用
    • 就从Java8开始吧(三)说一说Stream
    • 就从Java8开始吧(四)唠一唠Optional
    • 提高开发效率的奇技淫巧(一)lombok
    • maven概览
    • 就从Java8开始吧(五)新日期和时间API
    • 就从Java8开始吧(六)接口默认方法和静态方法
    • 提高开发效率的奇技(二)Intellij IDEA的进阶应用
    • 深入浅出java web(一):从根说起——servlet
    • 深入浅出java web (二):简述http协议
    • Java中equals()与==的区别详解
    • Java代理模式详解
    • Java中System.getProperty("user.dir")详解
    • Java格式工厂(一)——什么是二进制文件?
    • Java菜谱(二)——怎么求男学生的平均分?
    • Java菜谱(三)——常用数据结构转换及处理
    • Java菜谱(四)——怎么将10万条数据导出到excel?
    • Java菜谱(五)——怎么把字符串列表合并为一个字符串?
    • 函数式编程入门——拥抱函数式时代
    • Java Stream findFirst方法的空指针陷阱详解
      • 前言
      • 问题发现与分析
        • 🐛 问题现象
        • 🤔 初步分析的困惑
        • 🔍 错误信息分析
      • 基础知识回顾
        • Stream API 基础
        • Optional 类详解
        • findFirst 方法简介
      • 源码深度分析
        • findFirst 方法源码
        • FindOps 实现类分析
        • Optional.of 方法源码
        • 问题根因分析
      • 问题复现与演示
        • 完整的问题复现代码
        • 运行结果分析
      • 解决方案详解
        • 方案一:使用filter过滤null值
        • 方案二:使用Optional.ofNullable包装
        • 方案三:使用flatMap + Optional.ofNullable
        • 方案四:提前检查和处理
        • 方案五:自定义安全的findFirst方法
        • 方案对比总结
      • 最佳实践建议
        • 1. 🛡️ 防御性编程
        • 2. 🎯 明确业务语义
        • 3. 📝 使用有意义的方法名
        • 4. 🔍 使用静态分析工具
        • 5. 🧪 编写单元测试
      • 常见陷阱与注意事项
        • ⚠️ 陷阱1:混淆Optional.of和Optional.ofNullable
        • ⚠️ 陷阱2:在Stream中直接使用可能返回null的方法
        • ⚠️ 陷阱3:忽略Optional的正确用法
        • ⚠️ 陷阱4:过度使用Optional
        • ⚠️ 陷阱5:在集合中存储Optional
      • 总结
        • 🎯 核心要点回顾
        • 📚 学习收获
        • 🚀 进阶建议
        • 💡 最后的思考
  • Spring框架

  • 第三方库

  • Java
  • Java基础
吉森
2025-08-14
目录

Java Stream findFirst方法的空指针陷阱详解

函数式编程Stream APIOptional空指针异常 0 人阅读

# 前言

在Java 8引入Stream API后,函数式编程风格在Java开发中变得越来越流行。然而,在使用Stream API的过程中,开发者经常会遇到一些意想不到的陷阱。本文将深入分析一个在生产环境中实际遇到的问题:Stream的findFirst方法引发的空指针异常。

这个问题看似简单,但背后涉及到Optional类的设计原理、Stream API的内部实现机制,以及Java中null值处理的最佳实践。通过本文的学习,你将能够:

  • 理解Stream findFirst方法的工作原理
  • 掌握Optional类的正确使用方式
  • 学会避免常见的空指针异常陷阱
  • 掌握处理可能为null的Stream元素的最佳实践

# 问题发现与分析

# 🐛 问题现象

在一次生产环境测试中,我们遇到了一个令人困惑的空指针异常。问题代码如下:

List<String> idList = Arrays.asList("001", "002", "003");
Map<String, Person> personMap = new HashMap<>();
// 注意:personMap中只包含部分ID对应的Person对象
personMap.put("001", new Person("张三"));
personMap.put("003", new Person("李四"));
// "002"对应的Person不存在

// 问题代码:这里会抛出NullPointerException
Person person = idList.stream()
        .map(id -> personMap.get(id))  // 这里可能返回null
        .findFirst()                   // 这里抛出异常!
        .orElse(null);
1
2
3
4
5
6
7
8
9
10
11
12

# 🤔 初步分析的困惑

乍一看,这段代码似乎不应该抛出空指针异常:

  1. idList.stream() 创建了一个Stream
  2. .map(id -> personMap.get(id)) 将ID映射为Person对象
  3. .findFirst() 返回一个Optional对象
  4. .orElse(null) 从Optional中获取值,如果为空则返回null

按照这个逻辑,即使没有找到任何Person对象,findFirst()也应该返回Optional.empty(),然后orElse(null)返回null,不应该有空指针异常。

# 🔍 错误信息分析

但是,实际的错误堆栈明确指出:空指针异常发生在findFirst方法的调用中!

Exception in thread "main" java.lang.NullPointerException
    at java.util.Optional.of(Optional.java:258)
    at java.util.stream.FindOps$FindSink.accept(FindOps.java:146)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    ...
1
2
3
4
5

这个错误信息给了我们重要的线索:异常发生在Optional.of()方法中!

# 基础知识回顾

在深入分析问题之前,让我们先回顾一下相关的基础知识。

# Stream API 基础

Stream是Java 8引入的一个强大的数据处理API,它允许我们以声明式的方式处理数据集合。

// Stream的基本使用示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> result = names.stream()
    .filter(name -> name.length() > 3)  // 中间操作:过滤
    .map(String::toUpperCase)           // 中间操作:转换
    .collect(Collectors.toList());      // 终端操作:收集
1
2
3
4
5
6

Stream操作分为两类:

  • 中间操作(Intermediate Operations):如filter、map、sorted等,返回新的Stream
  • 终端操作(Terminal Operations):如findFirst、collect、forEach等,触发Stream的执行

# Optional 类详解

Optional是Java 8引入的一个容器类,用于优雅地处理可能为null的值。

// Optional的基本用法
Optional<String> optional1 = Optional.of("Hello");        // 包含非null值
Optional<String> optional2 = Optional.empty();            // 空Optional
Optional<String> optional3 = Optional.ofNullable(null);   // 可能为null的值

// 获取值的方法
String value1 = optional1.orElse("Default");              // 如果为空则返回默认值
String value2 = optional1.orElseGet(() -> "Computed");     // 如果为空则计算默认值
String value3 = optional1.orElseThrow();                  // 如果为空则抛出异常
1
2
3
4
5
6
7
8
9

重要区别:

  • Optional.of(value):要求value不能为null,否则抛出NullPointerException
  • Optional.ofNullable(value):允许value为null,如果为null则返回Optional.empty()

# findFirst 方法简介

findFirst()是Stream的一个终端操作,用于返回Stream中的第一个元素(如果存在)。

// findFirst的基本用法
Optional<String> first = Stream.of("a", "b", "c")
    .findFirst();  // 返回Optional.of("a")

Optional<String> empty = Stream.<String>empty()
    .findFirst();  // 返回Optional.empty()
1
2
3
4
5
6

# 源码深度分析

现在让我们深入分析findFirst方法的源码实现,理解问题的根本原因。

# findFirst 方法源码

// Stream接口中的findFirst方法
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    /**
     * Returns an {@code Optional} describing the first element of this stream,
     * or an empty {@code Optional} if the stream is empty.
     */
    Optional<T> findFirst();
}
1
2
3
4
5
6
7
8

# FindOps 实现类分析

findFirst的具体实现在FindOps类中:

// FindOps.java 中的关键代码
static final class FindSink<T, O> implements TerminalSink<T, O> {
    boolean hasValue;
    T value;

    @Override
    public void accept(T value) {
        if (!hasValue) {
            hasValue = true;
            this.value = value;  // 注意:这里直接赋值,可能为null!
        }
    }

    @Override
    public O get() {
        return hasValue ? Optional.of(value) : Optional.empty();
        //              ^^^^^^^^^^^^^^^^^
        //              问题就在这里!如果value为null,Optional.of()会抛出异常
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Optional.of 方法源码

// Optional.java 中的of方法
public static <T> Optional<T> of(T value) {
    return new Optional<>(Objects.requireNonNull(value));
    //                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //                    这里会检查value是否为null,如果为null则抛出NullPointerException
}

// Objects.requireNonNull 方法
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 问题根因分析

现在问题的根因就清楚了:

  1. Stream处理过程:idList.stream().map(id -> personMap.get(id))创建了一个可能包含null元素的Stream
  2. findFirst查找:findFirst()找到了Stream中的第一个元素,但这个元素是null
  3. Optional封装失败:findFirst()内部调用Optional.of(null)尝试封装null值
  4. 异常抛出:Optional.of()方法不允许null参数,抛出NullPointerException

关键理解:

  • "找到了一个null值" ≠ "没有找到值"
  • Optional.of(null) 会抛出异常
  • Optional.empty() 表示没有值

# 问题复现与演示

让我们通过完整的代码示例来复现这个问题:

# 完整的问题复现代码

import java.util.*;
import java.util.stream.*;

public class FindFirstNullPointerDemo {
    
    static class Person {
        private String name;
        
        public Person(String name) {
            this.name = name;
        }
        
        @Override
        public String toString() {
            return "Person{name='" + name + "'}";
        }
    }
    
    public static void main(String[] args) {
        // 准备测试数据
        List<String> idList = Arrays.asList("001", "002", "003");
        Map<String, Person> personMap = new HashMap<>();
        personMap.put("001", new Person("张三"));
        personMap.put("003", new Person("李四"));
        // 注意:"002"对应的Person不存在,get("002")会返回null
        
        System.out.println("=== 问题演示 ===");
        
        // 演示1:直接使用map + findFirst(会抛出异常)
        try {
            Person person = idList.stream()
                    .map(id -> {
                        Person p = personMap.get(id);
                        System.out.println("映射 " + id + " -> " + p);
                        return p;
                    })
                    .findFirst()
                    .orElse(null);
            System.out.println("结果: " + person);
        } catch (NullPointerException e) {
            System.out.println("❌ 抛出空指针异常: " + e.getMessage());
            e.printStackTrace();
        }
        
        System.out.println("\n=== 对比:没有null元素的情况 ===");
        
        // 演示2:所有元素都存在的情况(正常工作)
        List<String> validIdList = Arrays.asList("001", "003");
        Person validPerson = validIdList.stream()
                .map(id -> personMap.get(id))
                .findFirst()
                .orElse(null);
        System.out.println("✅ 正常结果: " + validPerson);
        
        System.out.println("\n=== 对比:空Stream的情况 ===");
        
        // 演示3:空Stream的情况(正常工作)
        Person emptyResult = Collections.<String>emptyList().stream()
                .map(id -> personMap.get(id))
                .findFirst()
                .orElse(null);
        System.out.println("✅ 空Stream结果: " + emptyResult);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# 运行结果分析

=== 问题演示 ===
映射 001 -> Person{name='张三'}
❌ 抛出空指针异常: null
java.lang.NullPointerException
    at java.util.Optional.of(Optional.java:258)
    at java.util.stream.FindOps$FindSink.accept(FindOps.java:146)
    ...

=== 对比:没有null元素的情况 ===
✅ 正常结果: Person{name='张三'}

=== 对比:空Stream的情况 ===
✅ 空Stream结果: null
1
2
3
4
5
6
7
8
9
10
11
12
13

从结果可以看出:

  • 当Stream中包含null元素时,findFirst()会抛出异常
  • 当Stream中所有元素都非null时,findFirst()正常工作
  • 当Stream为空时,findFirst()返回Optional.empty(),orElse(null)返回null

# 解决方案详解

针对这个问题,我们有多种解决方案,每种方案都有其适用场景。

# 方案一:使用filter过滤null值

思路:在调用findFirst()之前,先过滤掉null值。

// 解决方案1:过滤null值
Person person = idList.stream()
        .map(id -> personMap.get(id))
        .filter(Objects::nonNull)           // 过滤掉null值
        .findFirst()
        .orElse(null);

System.out.println("方案1结果: " + person);
1
2
3
4
5
6
7
8

优点:

  • 简单直观,易于理解
  • 性能较好,只需要一次过滤操作

缺点:

  • 如果所有映射结果都是null,会返回null而不是第一个null
  • 改变了原始的业务逻辑(跳过了null值)

# 方案二:使用Optional.ofNullable包装

思路:将映射结果包装成Optional,然后处理Optional流。

// 解决方案2:使用Optional.ofNullable
Person person = idList.stream()
        .map(id -> Optional.ofNullable(personMap.get(id)))  // 包装成Optional
        .filter(Optional::isPresent)                        // 过滤空Optional
        .map(Optional::get)                                 // 提取值
        .findFirst()
        .orElse(null);

System.out.println("方案2结果: " + person);
1
2
3
4
5
6
7
8
9

优点:

  • 明确表达了可能为null的语义
  • 类型安全,编译时就能发现问题

缺点:

  • 代码较为冗长
  • 性能开销稍大(创建Optional对象)

# 方案三:使用flatMap + Optional.ofNullable

思路:使用flatMap结合Optional.ofNullable,更加函数式的写法。

// 解决方案3:使用flatMap
Person person = idList.stream()
        .map(id -> Optional.ofNullable(personMap.get(id)))  // 映射为Optional
        .flatMap(Optional::stream)                          // 展平Optional
        .findFirst()
        .orElse(null);

System.out.println("方案3结果: " + person);
1
2
3
4
5
6
7
8

注意:Optional::stream是Java 9引入的方法,如果使用Java 8,可以这样写:

// Java 8兼容版本
Person person = idList.stream()
        .map(id -> Optional.ofNullable(personMap.get(id)))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .findFirst()
        .orElse(null);
1
2
3
4
5
6
7

优点:

  • 函数式编程风格
  • 语义清晰

缺点:

  • 需要Java 9+(使用Optional::stream)
  • 对初学者来说可能较难理解

# 方案四:提前检查和处理

思路:在Stream操作之前就处理可能的null情况。

// 解决方案4:提前检查
Person person = null;
for (String id : idList) {
    Person p = personMap.get(id);
    if (p != null) {
        person = p;
        break;
    }
}

System.out.println("方案4结果: " + person);
1
2
3
4
5
6
7
8
9
10
11

或者使用Stream但提前处理:

// 解决方案4变体:Stream + 提前检查
Person person = idList.stream()
        .filter(id -> personMap.containsKey(id))  // 提前检查key是否存在
        .map(id -> personMap.get(id))
        .findFirst()
        .orElse(null);

System.out.println("方案4变体结果: " + person);
1
2
3
4
5
6
7
8

优点:

  • 性能最好(避免了不必要的映射操作)
  • 逻辑清晰

缺点:

  • 不够函数式
  • 需要了解数据结构的特性(如Map的containsKey方法)

# 方案五:自定义安全的findFirst方法

思路:创建一个能够安全处理null值的findFirst方法。

public class SafeStreamUtils {
    
    /**
     * 安全的findFirst方法,能够处理Stream中的null值
     */
    public static <T> Optional<T> findFirstSafe(Stream<T> stream) {
        return stream
                .map(Optional::ofNullable)  // 将每个元素包装成Optional
                .findFirst()                // 找到第一个Optional
                .orElse(Optional.empty());  // 如果没找到则返回空Optional
    }
    
    /**
     * 查找第一个非null值
     */
    public static <T> Optional<T> findFirstNonNull(Stream<T> stream) {
        return stream
                .filter(Objects::nonNull)
                .findFirst();
    }
}

// 使用示例
Optional<Person> result = SafeStreamUtils.findFirstSafe(
    idList.stream().map(id -> personMap.get(id))
);
Person person = result.orElse(null);
System.out.println("方案5结果: " + person);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

优点:

  • 可重用,提高代码质量
  • 语义明确
  • 类型安全

缺点:

  • 需要额外的工具类
  • 团队需要了解这些自定义方法

# 方案对比总结

方案 适用场景 性能 可读性 类型安全
filter过滤null 只需要非null值 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Optional包装 需要明确null语义 ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
flatMap方式 函数式编程风格 ⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
提前检查 性能敏感场景 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
自定义工具 团队标准化 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

# 最佳实践建议

基于以上分析,这里提供一些使用Stream API的最佳实践建议:

# 1. 🛡️ 防御性编程

总是假设外部数据可能为null,在Stream操作中主动处理null值:

// ❌ 危险的写法
Person person = idList.stream()
        .map(personMap::get)  // 可能返回null
        .findFirst()          // 可能抛出异常
        .orElse(null);

// ✅ 安全的写法
Person person = idList.stream()
        .map(personMap::get)
        .filter(Objects::nonNull)  // 主动过滤null
        .findFirst()
        .orElse(null);
1
2
3
4
5
6
7
8
9
10
11
12

# 2. 🎯 明确业务语义

区分"没有找到"和"找到了null":

// 如果业务上需要区分这两种情况
Optional<Person> result = idList.stream()
        .map(id -> Optional.ofNullable(personMap.get(id)))
        .findFirst()
        .orElse(Optional.empty());

if (result.isPresent()) {
    System.out.println("找到了Person: " + result.get());
} else {
    System.out.println("没有找到任何Person");
}
1
2
3
4
5
6
7
8
9
10
11

# 3. 📝 使用有意义的方法名

创建语义明确的工具方法:

public class PersonService {
    
    /**
     * 根据ID列表查找第一个存在的Person
     */
    public Optional<Person> findFirstExistingPerson(List<String> idList) {
        return idList.stream()
                .map(personMap::get)
                .filter(Objects::nonNull)
                .findFirst();
    }
    
    /**
     * 根据ID列表查找第一个Person(可能为null)
     */
    public Optional<Person> findFirstPersonOrNull(List<String> idList) {
        return idList.stream()
                .map(id -> Optional.ofNullable(personMap.get(id)))
                .findFirst()
                .orElse(Optional.empty());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 4. 🔍 使用静态分析工具

配置IDE和静态分析工具来检测潜在的null问题:

// 使用注解标记可能为null的返回值
@Nullable
public Person getPersonById(String id) {
    return personMap.get(id);
}

// 使用注解标记不能为null的参数
public void processPerson(@NonNull Person person) {
    // 处理逻辑
}
1
2
3
4
5
6
7
8
9
10

# 5. 🧪 编写单元测试

针对边界情况编写测试:

@Test
public void testFindFirstWithNullValues() {
    List<String> idList = Arrays.asList("missing1", "existing", "missing2");
    Map<String, Person> personMap = Map.of("existing", new Person("Test"));
    
    // 测试过滤null值的情况
    Optional<Person> result = idList.stream()
            .map(personMap::get)
            .filter(Objects::nonNull)
            .findFirst();
    
    assertTrue(result.isPresent());
    assertEquals("Test", result.get().getName());
}

@Test
public void testFindFirstWithAllNullValues() {
    List<String> idList = Arrays.asList("missing1", "missing2");
    Map<String, Person> personMap = Collections.emptyMap();
    
    Optional<Person> result = idList.stream()
            .map(personMap::get)
            .filter(Objects::nonNull)
            .findFirst();
    
    assertFalse(result.isPresent());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 常见陷阱与注意事项

# ⚠️ 陷阱1:混淆Optional.of和Optional.ofNullable

// ❌ 错误:Optional.of不能接受null
Optional<String> opt1 = Optional.of(null);  // 抛出NullPointerException

// ✅ 正确:使用Optional.ofNullable
Optional<String> opt2 = Optional.ofNullable(null);  // 返回Optional.empty()
1
2
3
4
5

# ⚠️ 陷阱2:在Stream中直接使用可能返回null的方法

// ❌ 危险:Map.get()可能返回null
list.stream()
    .map(map::get)      // 可能产生null元素
    .findFirst();       // 可能抛出异常

// ✅ 安全:先检查或过滤
list.stream()
    .filter(map::containsKey)  // 先检查key是否存在
    .map(map::get)
    .findFirst();
1
2
3
4
5
6
7
8
9
10

# ⚠️ 陷阱3:忽略Optional的正确用法

// ❌ 错误:直接调用get()可能抛出异常
Optional<String> opt = getOptionalValue();
String value = opt.get();  // 如果opt为empty会抛出NoSuchElementException

// ✅ 正确:使用安全的方法
String value = opt.orElse("default");
// 或者
if (opt.isPresent()) {
    String value = opt.get();
    // 处理value
}
1
2
3
4
5
6
7
8
9
10
11

# ⚠️ 陷阱4:过度使用Optional

// ❌ 不必要:在私有方法中使用Optional
private Optional<String> processInternal(String input) {
    // 私有方法通常不需要Optional
    return Optional.ofNullable(input);
}

// ✅ 更好:直接返回可能为null的值
private String processInternal(String input) {
    return input;  // 调用者知道可能为null
}
1
2
3
4
5
6
7
8
9
10

# ⚠️ 陷阱5:在集合中存储Optional

// ❌ 不推荐:在集合中存储Optional
List<Optional<String>> list = new ArrayList<>();
list.add(Optional.of("value"));
list.add(Optional.empty());

// ✅ 更好:直接存储值,用null表示缺失
List<String> list = new ArrayList<>();
list.add("value");
list.add(null);
// 然后在使用时处理null
1
2
3
4
5
6
7
8
9
10

# 总结

# 🎯 核心要点回顾

  1. 问题根因:findFirst()内部使用Optional.of()封装找到的元素,而Optional.of()不允许null参数

  2. 关键区别:

    • "找到了null值" ≠ "没有找到值"
    • Optional.of(null) 会抛出异常
    • Optional.empty() 表示没有值
  3. 解决策略:

    • 使用filter(Objects::nonNull)过滤null值
    • 使用Optional.ofNullable()安全包装
    • 提前检查数据有效性
    • 创建安全的工具方法

# 📚 学习收获

通过这个案例,我们学到了:

  • Stream API的内部机制:理解了findFirst()的实现原理
  • Optional的正确使用:掌握了Optional.of()和Optional.ofNullable()的区别
  • 防御性编程思维:学会了在函数式编程中处理null值的最佳实践
  • 问题分析方法:通过源码分析定位问题根因的技巧

# 🚀 进阶建议

  1. 深入学习Stream API:了解更多Stream操作的内部实现
  2. 掌握函数式编程:学习更多函数式编程的设计模式
  3. 关注代码质量:使用静态分析工具和单元测试保证代码质量
  4. 团队规范:建立团队的编码规范和最佳实践

# 💡 最后的思考

这个看似简单的问题背后,体现了软件开发中的一个重要原则:细节决定成败。在使用任何API时,我们都应该:

  • 仔细阅读文档和源码
  • 理解API的设计意图和限制
  • 考虑边界情况和异常场景
  • 编写充分的测试用例

只有这样,我们才能写出健壮、可靠的代码,避免在生产环境中遇到意外的问题。


参考资料:

  • Java 8 Stream API官方文档 (opens new window)
  • Optional类官方文档 (opens new window)
  • Java函数式编程最佳实践 (opens new window)

希望这篇文章能帮助你更好地理解和使用Java Stream API!如果你有任何问题或建议,欢迎在评论区讨论。

编辑 (opens new window)
#函数式编程#Stream API#Optional#空指针异常
上次更新: 2025/08/14, 13:18:07
函数式编程入门——拥抱函数式时代
Electron + Spring Boot 桌面应用开发指南

← 函数式编程入门——拥抱函数式时代 Electron + Spring Boot 桌面应用开发指南→

最近更新
01
怎么写好技术文章?
08-25
02
CommonJS与ES模块:新手完全指南
08-21
03
深入理解Cookie生命周期:一次SSO单点登录问题的排查与解决
08-12
更多文章>
Theme by Vdoing | Copyright © 2024-2025 吉森 | MIT License | 吉ICP备17006653号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式