guava:支持数组(Object[])为Key的缓存实现

以前写过一篇博客《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》对Guava的缓存机制进一步进行了便利化封装。已经在我的项目中被广泛使用。

guava的LoadingCache在调用时要求只能有一个输入参数,算计后返回一个输出结果。

但是在实际应用中计算参数往不止一个,对于两个或三个输入参数的时候还可以用Pair,Triple之类的对象封装,但更多参数就不好搞了。

为了解决多参数计算结果缓存问题,最好能用对象数组(Object[])来做为Key,这样就可以广泛支持任意类型和数量参数的计算缓存。

但是我们知道一般情况下,数组是不能直接做为Map的KEY的,而guava的缓存机制正是基于Map实现的。

关于这个问题也有了解决方法,参见我更久以前写的博客《guava:Cache中使用数组(Object[],int[]...)作为KEY》

简单说在构建guava的com.google.common.cache.Cache实例时允许通过自己实现的com.google.common.base.Equivalence实例来指定如何对KEY进行比较,

参见 com.google.common.cache.CacheBuilder.keyEquivalence(Equivalence<Object> equivalence)方法

基于上面的技术准备,我更新了FunctionCached类的实现。增加内置类FunctionCached.Builder来实现FunctionCached实例的灵活构造,使用keyDeepEqual()方法指定对KEY执行深度比较

cpp 复制代码
		/**
		 * 对K执行比较时执行深度比较,<p>
		 * 对于K为数组类型时,必须指定深度比较
		 * @see CacheBuilder#keyEquivalence(Equivalence)  
		 */
		public Builder<K, V> keyDeepEqual() {
			ObjectDeepEquals.keyDeepEqual(cacheBuilder);
			return this;
		}

FunctionCached.java

java 复制代码
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.ObjectDeepEquals;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.Weigher;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
/**
 * 基于GUAVA {@link LoadingCache}实现类型转转换接口{@link Function},
 * 当一个输入K的计算结果已经存在于缓存时,直接返回,无需再次计算,
 * 避免对同一个输入数据多次重复计算。
 * 适用于输入值与结果恒定对应的场景
 * @author guyadong
 *
 * @param <K> 输入参数类型
 * @param <V> 输出参数类型
 */
public class FunctionCached<K,V> implements Function<K, V> {
	/**
	 * 输入值与计算结果的映射,计算结果可能是正常计算出的V值,也可能是异常(Exception)
	 */
	protected final LoadingCache<K, Object> cache;
	protected final Function<K, V> getterFunction;
	protected final V defaultValue;
	/**
	 * 构造方法
	 * @param getterFunction 原类型转换接口实现
	 * @param defaultValue    K为{@code null}时返回的默认值
	 * @param cacheBuilder    外部提供的CacheBuilder实例,为{@code null}则忽略
	 * @since 2.7.9
	 */
	public FunctionCached(final Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
		this.getterFunction = checkNotNull(getterFunction,"getterFunction is null");
		/**  输入参数不可以是缓存实例  */
		checkArgument(!(getterFunction instanceof FunctionCached),"getterFunction must not be instance of %s",getClass().getName());
		this.defaultValue = defaultValue;
		if(null == cacheBuilder ) {
			cacheBuilder= CacheBuilder.newBuilder();
		}
		this.cache = cacheBuilder.build(new CacheLoader<K, Object>(){
			@Override
			public Object load(K key) {
				try {
					return asCached(getterFunction.apply(key));
				} catch (Exception e) {
					return e;
				}
			}});
	}
	/**
	 * 构造方法
	 * @param getterFunction 原类型转换接口实现
	 * @param defaultValue    K为{@code null}时返回的默认值
	 */
	public FunctionCached(final Function<K, V> getterFunction,V defaultValue){
		this(getterFunction,defaultValue,null);
	}
	/**
	 * 构造方法,
	 * K 为{@code null}时返回{@code null}
	 * @param getterFunction 原类型转换接口实现
	 */
	public FunctionCached(Function<K, V> getterFunction){
		this(getterFunction, null);
	}
	/**
	 * 将 {@link LoadingCache}的值存储类型转为Value
	 * @param cached
	 */
	@SuppressWarnings("unchecked")
	protected V asValue(Object cached ){
		return (V) cached;
	}
	/**
	 * 将 value转为 {@link LoadingCache}的值存储类型
	 * @param value
	 */
	protected Object asCached(V value ){
		return value;
	}	
	
	/**
	 * 非缓存调用
	 * @param key
	 * @return {@code key}为{@code null}返回{@link #defaultValue}
	 */
	public V getUncached(K key){
		return null == key ? defaultValue: asValue(getterFunction.apply(key));
	}
	/**
	 * 缓存调用
	 * @param key
	 * @return {@code key}为{@code null}返回{@link #defaultValue}
	 */
	public V get(K key){
		if(null != key){
			Object value = cache.getUnchecked(key);
			if(value instanceof Exception) {
				Throwables.throwIfUnchecked((Exception)value);
				throw new RuntimeException((Exception)value);
			}
			return asValue(value);
		}
		return defaultValue;
	}
	
	/**
	 * 缓存调用
	 * @see #get(Object)
	 * @see com.google.common.base.Function#apply(java.lang.Object)
	 */
	@Override
	public V apply(K input) {
		return get(input);
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例,
	 * 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
	 * @param getterFunction
	 * @param defaultValue
	 * @param cacheBuilder 外部提供的CacheBuilder实例,为{@code null}则忽略
	 * @see #FunctionCached(Function, Object, CacheBuilder)
	 * @since 2.7.9
	 */
	public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
		if(getterFunction instanceof FunctionCached){
			return (FunctionCached<K, V>) getterFunction;
		}
		return new FunctionCached<K, V>(getterFunction, defaultValue,cacheBuilder); 
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例,
	 * 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
	 * @param getterFunction
	 * @param defaultValue
	 * @see #of(Function, Object, CacheBuilder)
	 */
	public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction,V defaultValue){
		return of(getterFunction, defaultValue,null); 
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例(默认实例为{@code null})
	 * @param getterFunction
	 * @see #of(Function, Object)
	 */
	public static <K, V> FunctionCached<K, V> of(Function<K, V> getterFunction){
		return of(getterFunction,null); 
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例,
	 * 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
	 * @param getterFunction 计算函数对象,允许返回{@code null}
	 * @param defaultValue
	 * @param cacheBuilder 外部提供的CacheBuilder实例,为{@code null}则忽略
	 * @see #of(Function, Object, CacheBuilder)
	 */
	public static <K, V> FunctionCached<K, Optional<V>> nullableOf(final Function<K, V> getterFunction,V defaultValue,CacheBuilder<Object,Object> cacheBuilder){
		checkNotNull(getterFunction,"getterFunction is null");
		return of(new Function<K, Optional<V>>(){
			@Override
			public Optional<V> apply(K input) {
				return  Optional.fromNullable(getterFunction.apply(input));
			}
		}, Optional.fromNullable(defaultValue),cacheBuilder); 
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例,
	 * 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
	 * @param getterFunction 计算函数对象,允许返回{@code null}
	 * @param defaultValue
	 * @see #nullableOf(Function, Object, CacheBuilder)
	 */
	public static <K, V> FunctionCached<K, Optional<V>> nullableOf(Function<K, V> getterFunction,V defaultValue){
		return nullableOf(getterFunction,defaultValue,null); 
	}
	/**
	 * 返回{@code getterFunction}的{@link FunctionCached}实例(默认实例为{@code null})
	 * @param getterFunction 计算函数对象,允许返回{@code null}
	 * @see #nullableOf(Function, Object, CacheBuilder)
	 */
	public static <K, V> FunctionCached<K, Optional<V>> nullableOf(Function<K, V> getterFunction){
		return nullableOf(getterFunction,null,null); 
	}
	public static<K, V>Builder<K, V> builder(){
		return new Builder<>();
	}
	public static class Builder<K, V>{
		private CacheBuilder<Object, Object> cacheBuilder;
		private Function<K, V> getterFunction;
		private V defaultValue;
		private boolean nullable;

		protected Builder() {
			this.cacheBuilder = CacheBuilder.newBuilder();
		}

		protected Builder(CacheBuilder<Object, Object> cacheBuilder, Function<K, V> getterFunction, V defaultValue,
				boolean nullable) {
			this.cacheBuilder = cacheBuilder;
			this.getterFunction = getterFunction;
			this.defaultValue = defaultValue;
			this.nullable = nullable;
		}
		public <V1> Builder<Object[],V1> invokeFunction( MethodInvokeFunction<V1> invokeFunction){
			return new Builder<Object[],V1>().keyDeepEqual().getterFunction(invokeFunction);
		}
		@SuppressWarnings("unchecked")
		public<K1, V1> Builder<K1, V1> getterFunction( Function<K1, V1> getterFunction){
			return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),(V1)defaultValue,this.nullable);
		}
		public <K1, V1> Builder<K1, V1> getterFunction( Function<K1, V1> getterFunction,V1 defaultValue){
			return new Builder<K1, V1>(this.cacheBuilder,checkNotNull(getterFunction,"getterFunction is null"),defaultValue,this.nullable);
		}

		/**
		 * 指定当{@link #getterFunction}返回值为{@code null}或{@link FunctionCached#get(Object)}参数为{@code null}时返回的默认值,
		 * 默认为{@code null}
		 * @param defaultValue
		 */
		public Builder<K, V> defaultValue(V defaultValue) {
			this.defaultValue = defaultValue;
			return this;
		}
		/**
		 * 对K执行比较时执行深度比较,<p>
		 * 对于K为数组类型时,必须指定深度比较
		 * @see CacheBuilder#keyEquivalence(Equivalence)  
		 */
		public Builder<K, V> keyDeepEqual() {
			ObjectDeepEquals.keyDeepEqual(cacheBuilder);
			return this;
		}
		/**
		 * 允许 {@link #getterFunction}返回值为{@code null}
		 */
		public Builder<K, V> nullable() {
			nullable = true;
			return this;
		}
		/**
		 * @see CacheBuilder#initialCapacity(int)
		 */
		public Builder<K, V> initialCapacity(int initialCapacity) {
			cacheBuilder.initialCapacity(initialCapacity);
			return this;
		}

		/**
		 * @see CacheBuilder#concurrencyLevel(int)
		 */
		public Builder<K, V> concurrencyLevel(int concurrencyLevel) {
			cacheBuilder.concurrencyLevel(concurrencyLevel);
			return this;
		}

		/**
		 * @see CacheBuilder#maximumSize(long)
		 */
		public Builder<K, V> maximumSize(long size) {
			cacheBuilder.maximumSize(size);
			return this;
		}

		/**
		 * @see CacheBuilder#maximumWeight(long)
		 */
		public Builder<K, V> maximumWeight(long weight) {
			cacheBuilder.maximumWeight(weight);
			return this;
		}

		/**
		 * @see CacheBuilder#weigher(Weigher)
		 */
		public <K1, V1> Builder<K, V> weigher(Weigher<? super K1, ? super V1> weigher) {
			cacheBuilder.weigher(weigher);
			return this;
		}

		/**
		 * @see CacheBuilder#weakValues()
		 */
		public Builder<K, V> weakValues() {
			cacheBuilder.weakValues();
			return this;
		}

		/**
		 * @see CacheBuilder#softValues()
		 */
		public Builder<K, V> softValues() {
			cacheBuilder.softValues();
			return this;
		}

		/**
		 * @see CacheBuilder#expireAfterWrite(long, TimeUnit)
		 */
		public Builder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
			cacheBuilder.expireAfterWrite(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#expireAfterAccess(long, TimeUnit)
		 */
		public Builder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
			cacheBuilder.expireAfterAccess(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#refreshAfterWrite(long, TimeUnit)
		 */
		public Builder<K, V> refreshAfterWrite(long duration, TimeUnit unit) {
			cacheBuilder.refreshAfterWrite(duration, unit);
			return this;
		}

		/**
		 * @see CacheBuilder#ticker(Ticker)
		 */
		public Builder<K, V> ticker(Ticker ticker) {
			cacheBuilder.ticker(ticker);
			return this;
		}

		/**
		 * @see CacheBuilder#removalListener(RemovalListener)
		 */
		public Builder<K, V> removalListener(RemovalListener<? super K, ? super V> listener) {
			cacheBuilder.removalListener(listener);
			return this;
		}
		/**
		 * @see CacheBuilder#recordStats()
		 */
		public Builder<K, V> recordStats() {
			cacheBuilder.recordStats();
			return this;
		}

		public FunctionCached<K,V> build() {
			if(nullable) {
				return new FunctionCached<K,V>(getterFunction, defaultValue, cacheBuilder) {
					final Optional<V> defOpt = Optional.fromNullable(defaultValue);
					@SuppressWarnings("unchecked")
					@Override
					protected V asValue(Object cached) {
						return ((Optional<V>)cached).or(defOpt).orNull();
					}

					@Override
					protected Object asCached(V value) {
						return Optional.fromNullable(value);
					}
				};
			}
			return new FunctionCached<>(getterFunction, defaultValue, cacheBuilder);
		}
	}
	public static class MethodInvokeFunction<T> implements Function<Object[], T>{
		private final Method method;
		private final Object target;
		public MethodInvokeFunction(Object target, Method method) {
			this.method = checkNotNull(method,"method is null") ;
			if(Modifier.isStatic(method.getModifiers())) {
				this.target = target;
			}else {
				this.target = checkNotNull(target,"target is null");
			}
		}
		public MethodInvokeFunction(Method method) {
			this(null,method);
		}
		public MethodInvokeFunction(Object target, Class<?>clazz,String name,Class<?>...parameterTypes) {
			this(target,methodOf(clazz,name,null, parameterTypes));
		}
		public MethodInvokeFunction(Class<?>clazz,String name,Class<?>...parameterTypes) {
			this(null,methodOf(clazz,name,true, parameterTypes));
		}
		public MethodInvokeFunction(Object target, String name,Class<?>...parameterTypes) {
			this(target,methodOf(checkNotNull(target,"target is null").getClass(),name,false, parameterTypes));
		}
		@SuppressWarnings("unchecked")
		@Override
		public T apply(Object[] input) {
			try {
				if(!method.isAccessible()) {
					method.setAccessible(true);
				}
				return (T) method.invoke(target, input);
				
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
		private static Method methodOf(Class<?>clazz, String name,Boolean staticRequired, Class<?>...parameterTypes) {
			try {
				checkArgument(null!=parameterTypes && parameterTypes.length>0,"INVALID parameterTypes.length,>0 required");
				Method method = checkNotNull(clazz,"clazz is null").getMethod(checkNotNull(name,"name is null"), parameterTypes );
				if(Boolean.TRUE == staticRequired) {
					checkArgument(Modifier.isStatic(method.getModifiers()),"%s is not static method",method.toString());					
				}else if(Boolean.FALSE == staticRequired) {
					checkArgument(!Modifier.isStatic(method.getModifiers()),"%s is static method",method.toString());					
				}
				return method;
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
	}
}

调用示例

java 复制代码
	private static final FunctionCached<Object[], LinkedHashSet<Class<?>>> FIND_CLASSES_CACHED = 
			/** builder模式构建 FunctionCached实例*/
			FunctionCached.builder()
				/** 
				 * 值存储指定弱引用(WeakReference)模式 
				 * 参见上一篇博客:
				 * 《guava:LoadingCache缓存机制支持弱引用(WeakReference)》
				 * https://blog.csdn.net/10km/article/details/139060089
				 */
				.weakValues()
				/** 
				 * Key(Object[])执行深度比较 
				 * 参见博客:
				 * 《guava:Cache中使用数组(Object[],int[]...)作为KEY》
				 * https://blog.csdn.net/10km/article/details/103072235
				 */
				.keyDeepEqual()
				.getterFunction(new Function<Object[], LinkedHashSet<Class<?>>>() {
					@SuppressWarnings("unchecked")
					@Override
					public LinkedHashSet<Class<?>> apply(Object[] input) {
						/** 调用方法执行多参数计算 */
						return findClasses0((Iterable<Class<?>>)input[0],
								(Supplier<RangeFilter>)input[1],
								(Iterable<Predicate<Class<?>>>)input[2]);
					}
				}).build();
	private static LinkedHashSet<Class<?>> findClasses0(Iterable<Class<?>> classes,
				Supplier<RangeFilter> finisher, Iterable<Predicate<Class<?>>> filters){
		// 计算实现代码
	}

完整代码参见码云仓库:https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/utils/FunctionCached.java

参考资料

《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》
《guava:Cache中使用数组(Object[],int[]...)作为KEY》
《guava:LoadingCache缓存机制支持弱引用(WeakReference)》

相关推荐
Full Stack Developme5 分钟前
java.nio 包详解
java·python·nio
零千叶21 分钟前
【面试】Java JVM 调优面试手册
java·开发语言·jvm
代码充电宝30 分钟前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表
li37149089034 分钟前
nginx报400bad request 请求头过大异常处理
java·运维·nginx
摇滚侠38 分钟前
Spring Boot 项目, idea 控制台日志设置彩色
java·spring boot·intellij-idea
Aevget1 小时前
「Java EE开发指南」用MyEclipse开发的EJB开发工具(二)
java·ide·java-ee·eclipse·myeclipse
黄昏晓x1 小时前
C++----多态
java·jvm·c++
Brookty2 小时前
【算法】前缀和
java·学习·算法·前缀和·动态规划
少许极端2 小时前
算法奇妙屋(七)-字符串操作
java·开发语言·数据结构·算法·字符串操作
懒羊羊不懒@2 小时前
Java基础语法—字面量、变量详解、存储数据原理
java·开发语言