Este dilema se puede resolver de diferentes maneras, pero en este caso te damos la resolución más completa para nosotros.
Solución:
En Java 8 puedes usar ConcurrentHashMap.computeIfAbsent
:
Map cache = new ConcurrentHashMap<>();
Integer addOne(Integer x)
return cache.computeIfAbsent(x -> x + 1);
DZone tiene un buen tutorial que proporciona una solución que funcionará para cualquier método:
El
Memoizer
la clase es bastante simple:public class Memoizer
private final Map cache = new ConcurrentHashMap<>(); private Memoizer() private Function doMemoize(final Function function) return input -> cache.computeIfAbsent(input, function::apply); public static Function memoize(final Function function) return new Memoizer ().doMemoize(function); Usar esta clase también es extremadamente simple:
Integer longCalculation(Integer x) try Thread.sleep(1_000); catch (InterruptedException ignored) return x * 2; Function
f = this::longCalculation; Function g = Memoizer.memoize(f); public void automaticMemoizationExample() long startTime = System.currentTimeMillis(); Integer result1 = g.apply(1); long time1 = System.currentTimeMillis() - startTime; startTime = System.currentTimeMillis(); Integer result2 = g.apply(1); long time2 = System.currentTimeMillis() - startTime; System.out.println(result1); System.out.println(result2); System.out.println(time1); System.out.println(time2); ejecutando el
automaticMemoizationExample
método producirá el siguiente resultado:2 2 1000 0
Puedes memorizar cualquier función con Java 8 MethodHandle
s y lambdas si está dispuesto a renunciar a la seguridad de tipos en los parámetros:
public interface MemoizedFunction
V call(Object... args);
private static class ArgList
public Object[] args;
@Override
public boolean equals(Object o)
if (this == o)
return true;
if (!(o instanceof ArgList))
return false;
ArgList argList = (ArgList) o;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(args, argList.args);
@Override
public int hashCode()
return args != null ? Arrays.hashCode(args) : 0;
public static MemoizedFunction memoizeFunction(Class super V> returnType, Method method) throws
IllegalAccessException
final Map memoizedCalls = new HashMap<>();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.unreflect(method)
.asSpreader(Object[].class, method.getParameterCount());
return args ->
ArgList argList = new ArgList();
argList.args = args;
return memoizedCalls.computeIfAbsent(argList, argList2 ->
try
//noinspection unchecked
return (V) methodHandle.invoke(args);
catch (Throwable throwable)
throw new RuntimeException(throwable);
);
;
Ejemplo de trabajo
Esto crea una lambda de aridad variable que encierra la función y es casi tan rápido como llamar a la función directamente (es decir, no ocurre ningún reflejo dentro de call(Object...args)
) después de construir la lambda ya que estamos usando MethodHandle.invoke()
en vez de Method.invoke()
.
Todavía puede hacer esto sin lambdas (reemplazar con clases anónimas) y MethodHandles (reemplazar con Method.invoke), pero habrá penalizaciones de rendimiento que harán que esto sea menos atractivo para el código consciente del rendimiento.
Comentarios y valoraciones de la guía
Agradecemos que quieras añadir valor a nuestro contenido asistiendo con tu veteranía en las interpretaciones.