Java中Supplier延迟生成值的原因及分析

 更新时间:2026年01月05日 09:12:52   作者:努力学习的明  
延迟生成值(LazyValueGeneration)是指将值的计算或生成过程推迟到真正需要使用该值时才执行,避免不必要的计算,提升程序的性能和资源利用率,Java中的Supplier和Optional接口可以实现延迟生成

在编程中,延迟生成值(Lazy Value Generation) 是指将值的计算或生成过程推迟到真正需要使用该值时才执行。

这一机制的核心是避免不必要的计算,提升程序的性能和资源利用率。结合 SupplierOptional 的使用场景,我们可以从以下几个方面理解延迟生成值:

一、核心概念:为什么需要延迟?

假设你需要一个默认值,但这个默认值的生成可能:

  • 耗时(如数据库查询、网络请求)。
  • 耗资源(如创建大型对象、复杂计算)。
  • 依赖状态(如需要先判断某个条件是否成立)。

如果提前生成默认值,可能会导致:

  • 性能浪费:即使最终不需要使用默认值(比如 Optional 中有值),也会白白执行计算。
  • 逻辑错误:默认值的生成可能依赖后续才会变化的状态。

延迟生成的目标:仅在必要时执行计算,减少无意义的开销。

二、通过Supplier实现延迟生成

Supplier 是 Java 函数式接口(@FunctionalInterface),仅定义一个无参数方法 get(),用于返回一个值。

它的关键作用是:将值的生成逻辑封装在 get(),而不立即执行。

示例:模拟耗时操作

import java.util.Optional;
import java.util.UUID;

public class LazyDemo {
    public static void main(String[] args) {
        Optional<String> optional = Optional.empty(); // 假设无值

        // 错误写法:提前执行生成逻辑(无论是否需要)
        String eagerValue = generateEagerly(); // 立即执行
        String value1 = optional.orElse(eagerValue); 

        // 正确写法:延迟执行生成逻辑(仅在必要时)
        String value2 = optional.orElseGet(() -> generateLazily()); // 仅当optional为空时执行
    }

    // 提前生成(立即执行)
    private static String generateEagerly() {
        System.out.println("执行提前生成逻辑");
        return UUID.randomUUID().toString();
    }

    // 延迟生成(通过Supplier封装)
    private static String generateLazily() {
        System.out.println("执行延迟生成逻辑");
        return UUID.randomUUID().toString();
    }
}

执行结果对比

optional 为空时:

  • generateEagerly() 会在调用 orElse 前就执行,输出 执行提前生成逻辑
  • generateLazily() 仅在调用 orElseGetoptional 为空时执行,输出 执行延迟生成逻辑

optional 有值时(如 Optional.of("有值")):

  • generateEagerly() 仍然会执行(浪费计算)。
  • generateLazily() 不会执行(节省资源)。

三、延迟生成的典型应用场景

1.避免不必要的计算

// 假设getExpensiveData()是耗时操作
Optional<String> data = fetchData(); 

// 错误:无论data是否有值,都会执行getExpensiveData()
String result = data.orElse(getExpensiveData()); 

// 正确:仅当data为空时,才执行getExpensiveData()
String result = data.orElseGet(() -> getExpensiveData()); 

2.依赖上下文的动态默认值

public class UserService {
    private final Database database;

    public User getUser(String userId) {
        Optional<User> user = database.queryUser(userId);
        // 仅当用户不存在时,创建默认用户(依赖当前时间)
        return user.orElseGet(() -> createDefaultUser(LocalDateTime.now()));
    }

    private User createDefaultUser(LocalDateTime timestamp) {
        // 需要当前时间作为参数,延迟生成时才能获取最新值
        return new User("default", timestamp);
    }
}
  • 如果提前生成默认用户(如 orElse(createDefaultUser(...))),可能使用过时的时间戳。
  • 延迟生成时,createDefaultUser 能获取当前最新的上下文数据(如实时时间)。

3.流式编程中的延迟计算

在 Java Stream 中,某些操作(如 filtermap)是惰性的(Lazy),仅在终端操作(如 forEachcollect)时才执行。Supplier 可以配合这种特性优化性能:

List<String> data = ...;

// 延迟生成随机数,仅在需要时(如过滤后的数据满足条件)才生成
data.stream()
    .filter(s -> s.startsWith("A"))
    .findFirst()
    .orElseGet(() -> UUID.randomUUID().toString()); // 仅当无匹配元素时生成

四、延迟生成与立即生成的对比

场景延迟生成(orElseGet)立即生成(orElse)
性能仅在必要时执行计算,节省资源无论是否需要,均提前执行计算
适用场景默认值生成耗时、依赖动态状态默认值已提前存在或生成成本极低
代码可读性需通过 lambda 封装逻辑,稍显复杂直接传入值,代码更简洁
线程安全性每次调用 get() 都会重新计算(需注意)提前生成一次,后续使用同一值

五、总结:延迟生成的本质

延迟生成值的核心是将计算逻辑与值的使用时机解耦,通过 Supplier 等工具将 “生成值” 的动作推迟到真正需要的时刻。这一思想在 Java 中广泛应用于:

  • Optional 的默认值生成(orElseGet)。
  • 流式编程的惰性操作。
  • 框架中的依赖注入(如 Spring 的 Lazy 注解)。
  • 缓存场景的按需加载(如 ConcurrentHashMap.computeIfAbsent)。

通过延迟生成,可以显著提升程序的效率,避免 “过早计算” 带来的性能损耗,尤其在处理高成本操作或动态依赖时优势明显。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java中获取webapp路径问题详解

    Java中获取webapp路径问题详解

    这篇文章主要介绍了Java中获取webapp路径问题详解,WebApp是指基于Web的 系统和 应用,其作用是向广大的最终用户发布一组复杂的内容和功能,本文详解了关于获取路径时候可能出现的问题,需要的朋友可以参考下
    2023-07-07
  • Java编程语言特性和优势

    Java编程语言特性和优势

    这篇文章主要介绍了Java编程语言特性和优势,Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,同时也增加了垃圾回收机制,释放掉不被使用的内存空间,解决了管理内存空间的烦恼,下面来聊聊Java编程语言特性和优势吧
    2022-01-01
  • Java线程协作的两种方式小结

    Java线程协作的两种方式小结

    Java中线程协作的最常见的两种方式是利用Object.wait()、Object.notify()和使用Condition,本文主要介绍了Java线程协作的两种方式小结,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Java基础之Unsafe内存操作不安全类详解

    Java基础之Unsafe内存操作不安全类详解

    Java是面向对象语言,在使用Java编程时,大多数情况下都不会直接操作内存,而且Java也不提倡直接操作内存,但是Java中到底有没有可以直接操作内存的工具类呢?有!Java中提供Unsafe类可以用来来直接操作内存,文中详细介绍了Unsafe内存操作不安全类,需要的朋友可以参考下
    2021-06-06
  • Java多线程中的ThreadLocal应用场景及问题解读

    Java多线程中的ThreadLocal应用场景及问题解读

    这篇文章主要介绍了Java多线程中的ThreadLocal应用场景及问题解读,ThreadLocal这个类在多线程并发中主要的使用场景是什么呢,我们都知道多线程并发问题实际就是多个线程对公共资源访问和修改问题,需要的朋友可以参考下
    2023-12-12
  • Java泛型继承原理与用法详解

    Java泛型继承原理与用法详解

    这篇文章主要介绍了Java泛型继承原理与用法,结合实例形式分析了java泛型继承的相关原理与实现技巧,需要的朋友可以参考下
    2019-07-07
  • SpringBoot算法实现数据加密传输

    SpringBoot算法实现数据加密传输

    这篇文章主要为大家详细介绍了SpringBoot算法实现数据加密传输的相关知识,本文主要是混合加密,前端 SM2 + SM4,后端 Spring Boot + Hutool 解密,希望对大家有所帮助
    2026-03-03
  • Java自定义注解导入和导出合并一对多单元格工具类方式

    Java自定义注解导入和导出合并一对多单元格工具类方式

    文章主要介绍了excel工具类对poi的封装,包括导入依赖、创建注解、IExcelUtils工具类代码等内容,示例通过订单、订单明细、追溯码三类实体进行导入和导出测试,展示了日期转换、排序、隐藏字段等功能的应用
    2026-04-04
  • MyBatis注解CRUD与执行流程深入探究

    MyBatis注解CRUD与执行流程深入探究

    这篇文章主要介绍了MyBatis注解CRUD与执行流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02
  • Java String转换时为null的解决方法

    Java String转换时为null的解决方法

    这篇文章主要介绍了Java String转换时为null的解决方法,需要的朋友可以参考下
    2017-07-07

最新评论