前言
在计算机之中,用“空间换时间”是计算机一个常用的优化策略。
本文通过java语言和项目开发的例子,更加深入地理解这个概念
例子说明
Java语言中使用空间换时间的例子
- Integer类中,IntegerCache预缓存-128到127的整数对象。
- ThreadLocal,每个 Thread 内部维护一个 ThreadLocalMap,存储该线程独有的变量副本。常用于连接池的线程绑定、日志追踪 ID等。注:ThreadLocal的变量使用完需主动remove。
- 不单独创建线程,而是通过管理线程池使用线程资源。
项目开发中使用空间换时间的例子
下面我用简单的几个来进行说明。
- 使用额外空间减少重复计算
public class SpaceTimeExample {// 递归版本(指数级)public static long fibonacciRecursive(int n) {if (n <= 1) return n;return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);}// 缓存版本(线性)public static long fibonacciCached(int n) {// 使用额外空间存储中间结果long[] fib = new long[n + 1];fib[0] = 0L; fib[1] = 1L;for (int i = 2; i <= n; i++) {// 避免重复计算fib[i] = fib[i - 1] + fib[i - 2];}return fib[n];}// 性能测试(n=40)public static void main(String[] args) {int n = 40;long start = System.nanoTime();long result1 = fibonacciRecursive(n);long time1 = System.nanoTime() - start;start = System.nanoTime();long result2 = fibonacciCached(n);long time2 = System.nanoTime() - start;System.out.printf("递归版本: %d ns (%.2f 秒)%n", time1, time1 / 1e9);System.out.printf("缓存版本: %d ns (%.2f 毫秒)%n", time2, time2 / 1e6);System.out.printf("性能提升: %.0f 倍%n", (double) time1 / time2);}
}
在我的电脑上(Apple M1 芯片,16G内存)
递归版本: 293748834 ns (0.29 秒)
缓存版本: 13750 ns (0.01 毫秒)
性能提升: 21364 倍
2. 用字典表减少查询次数
public enum StatusEnum {DRAFT("草稿"),PENDING("待审批"),APPROVED("审批通过"),REJECTED("审批拒绝"),WITHDRAW("主动取消"),FROZEN("已冻结"),;/*** 说明*/private final String desc;StatusEnum(String desc) {this.desc = desc;}public String getDesc() {return desc;}/*** 可以再次提交审批的状态*/public Boolean canReApprovedStatus(StatusEnum status) {if (StatusEnum.DRAFT == status || StatusEnum.REJECTED == status || StatusEnum.WITHDRAW == status) {return true;} else {return false;}}/*** 通过空间换时间的版本, 将可以再次提交审批的状态存入字典*/public static final Set<StatusEnum> CAN_RE_APPROVED_STATUS_SET =EnumSet.of(StatusEnum.DRAFT, StatusEnum.REJECTED, StatusEnum.WITHDRAW);public static Boolean betterCanReApprovedStatus(StatusEnum status) {return CAN_RE_APPROVED_STATUS_SET.contains(status);}}
- 预加载资源
/*** 预编译正整数的正则*/public static final Pattern PATTERN = Pattern.compile("^\\d+$");public static Boolean match(String number) {return PATTERN.matcher(number).matches();}public static void main(String[] args) {String number = "123.45";System.out.printf("该数字:%s是否为正整数:%s%n", number, match(number));String number2 = "123";System.out.printf("该数字:%s是否为正整数:%s%n", number2, match(number2));}
适用场景
- 数据查询重复率高
- 数据占用空间有限,不会引起OOM
- 代码高频访问,优化后提升明显
风险
- 在内存稀缺的场景下(如嵌入式开发)容易引发OOM,在原始数据量巨大的情况下,可能会耗尽内存。
- 若源数据重复率很低,大量的数据冷启动容易引起内存等资源的浪费。
