Java小技巧:创建带缓存的过程

在平常开发中,我们经常遇到这样一类过程:有一定计算量,被频繁调用,但对于任意一个参数,结果是恒定的(换句话说,就是纯函数),为了减少频繁调用的性能开销,我们可能会写出这样的代码:

java 复制代码
public class ExampleUnitTest {

    Map<Integer, String> cacheMap = new HashMap<>();

    public String doTaskWithCache(Integer i) {
        String s = cacheMap.get(i); // 先检查是否有缓存
        if (s == null) {
            s = calculateResult(i);
            cacheMap.put(i, s);  // 计算结果存到缓存里
        }
        return s;
    }
}

但是,这样写的话,每次有需要带缓存的函数,创建缓存以及检查结果是否已缓存的那代码就得重写一遍,想想就有些麻烦,本文就介绍一个简单的工具Memorizer来简化这个过程。

Memorizer 的实现

因为实现代码比较少,我们看实现代码

java 复制代码
public class Memorizer<T, V> {
    public static <T, V> Function<T, V> memorize(Function<T, V> function) {
        Map<T, V> cacheMap = new HashMap<>();
        return (T t) -> Optional.ofNullable(cacheMap.get(t)).orElseGet(() -> {
            V value = function.apply(t);
            cacheMap.put(t, value);
            return value;
        });
    }

    public static <T, V> Function<T, V> weakMemorize(Function<T, V> function) {
        Map<T, V> cacheMap = new WeakHashMap<>();
        return (T t) -> Optional.ofNullable(cacheMap.get(t)).orElseGet(() -> {
            V value = function.apply(t);
            cacheMap.put(t, value);
            return value;
        });
    }
}

这个工具有两个方法,都接受一个单参函数作为参数,并返回一个同类型的单参函数,意思是:传入要缓存的函数,返回该函数的带缓存的版本。

weakMemorize方法返回的函数的缓存可能会被回收,适合那些内存资源紧张或者想减少内存压力的场景,不过这也会导致一定概率的缓存失效的情况。

Memorizer 的使用

我们用``来改写上面的例子,就成了:

java 复制代码
public class ExampleUnitTest {
    private Function<Integer, String> doTaskWithCache = (i) -> calculateResult(i);
}

写在最后

这里只给出了单参数的实现,如果要考虑多参数的缓存,可以尝试用下面这种方式扩展:

java 复制代码
public class Memorizer<T, V> {
    public static <T1, T2, V, K> BiFunction<T1, T2, V> memorize(BiFunction<T1, T2, V> function, BiFunction<T1, T2, K> keyExtractor) {
        Map<K, V> cacheMap = new HashMap<>();
        return (t1, t2) -> {
            K key = keyExtractor.apply(t1, t2);
            return Optional.ofNullable(cacheMap.get(key)).orElseGet(() -> {
                V value = function.apply(t1, t2);
                cacheMap.put(key, value);
                return value;
            });
        };
    }

    public static <T1, T2, V, K> BiFunction<T1, T2, V> weakMemorize(BiFunction<T1, T2, V> function, BiFunction<T1, T2, K> keyExtractor) {
        Map<K, V> cacheMap = new WeakHashMap<>();
        return (t1, t2) -> {
            K key = keyExtractor.apply(t1, t2);
            return Optional.ofNullable(cacheMap.get(key)).orElseGet(() -> {
                V value = function.apply(t1, t2);
                cacheMap.put(key, value);
                return value;
            });
        };
    }
}
相关推荐
isysc11 小时前
面了一个校招生,竟然说我是老古董
java·后端·面试
黄林晴3 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我3 小时前
flutter 之真手势冲突处理
android·flutter
法的空间4 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止4 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭4 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
道可到4 小时前
Java 反射现代实践速查表(JDK 11+/17+)
java
jctech4 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户2018792831674 小时前
为何Handler的postDelayed不适合精准定时任务?
android
道可到4 小时前
Java 反射现代实践指南(JDK 11+ / 17+ 适用)
java