HashMap con valores débiles
Estoy implementando un caché para objetos almacenados de forma persistente. La idea es:
- Método
getObjectFromPersistence(long id); ///Takes about 3 seconds
getObjectFromPersistence(long id); ///Takes about 3 seconds
- Método
getObjectFromCache(long id) //Instantly
Y tiene un método: getObject(long id)
con el siguiente pseudocódigo:
- Cómo utilizar setEmptyView () con el diseño de lista personalizada en ListFragment
- Transferencia suave de WiFi en Android
- Visualización de elementos de menú en la barra de acciones de Android ICS
- ¿De dónde viene Enum.values ()?
- Libgdx extraño modelado - error de profundidad?
synchronized(this){ CustomObject result= getObjectFromCache(id) if (result==null){ result=getObjectFromPersistence(id); addToCache(result); } return result; }
Pero tengo que permitir que el objeto CustomObject sea recopilado por el recolector de basura. Hasta ahora estaba usando un HashMap<Long,WeakReference<CustomObject>
para la implementación. El problema es que durante el tiempo que el HashMap se llena de WeakReferences
vacío.
He comprobado WeakHashMap pero allí las llaves son débiles (y los valores son todavía referencias fuertes) así que tener los largos con WeakReferences no tiene sentido.
¿Cuál es la mejor solución para resolver este problema? ¿Hay algún "WeakHashMap inverso" o algo similar?
Gracias
- Error al crear la propia biblioteca de tarro para el proyecto de Android
- Cadena aleatoria de la lista de matrices
- Líneas de desplazamiento de borde azul de listview. Androide
- ¿Cómo puedo crear un módulo de prueba de Android en IntelliJ 13 para un proyecto Android de Gradle?
- Se accede a la variable dentro de la clase interna. Necesita ser declarado final
- Android Detener la notificación de brindis por programación?
- Java JNI vs. Android NDK
- Casos de uso adecuados para Android UserManager.isUserAGoat ()?
Usted puede utilizar el Guava MapMaker
para esto:
ConcurrentMap<Long, CustomObject> graphs = new MapMaker() .weakValues() .makeMap();
Incluso puede incluir la parte de cálculo sustituyendo makeMap()
por esto:
.makeComputingMap( new Function<Long, CustomObject>() { public CustomObject apply(Long id) { return getObjectFromPersistence(id); } });
Puesto que lo que estás escribiendo se parece mucho a una memoria caché, el caché más nuevo y más especializado (construido a través de CacheBuilder
) puede ser aún más relevante para ti. No implementa la interfaz de Map
directamente, pero proporciona incluso más controles que puede que desee para una caché.
¿Has probado android.util.LruCache
(es una clase SDK11 pero también está en el paquete de compatibilidad como android.support.v4.util.LruCache
). No implementa java.util.Map
pero funciona como un mapa y se puede definir la cantidad de memoria que se necesitará y se descargará antiguos (objetos en caché no utilizados por sí mismo).
Una WeakReference
se añade a su ReferenceQueue
suministrado en el momento de la construcción cuando se recoge su referencia.
Puede poll
la ReferenceQueue
cada vez que acceda a la caché y mantener una HashMap<WeakReference<CustomObject>,Long>
para saber qué entrada quitar si se encuentra una referencia en la cola.
De forma alternativa, si la caché no se utiliza con frecuencia, puede ver la cola en un subproceso separado.
Usted podría comenzar una "limpieza" – Hilo de vez en cuando. Tal vez si el tamaño de su mapa supera un umbral, pero como máximo cada 5 minutos … algo así.
Mantenga los ciclos de limpieza cortos para no bloquear la funcionalidad principal.
También puede probar WeakValueHashMap desde jboss-common http://docs.jboss.org/jbossas/javadoc/4.0.2/org/jboss/util/collection/WeakValueHashMap.java.html
Creo que la mejor opción (si una dependencia de Guava es indeseable) sería usar una subclase personalizada de WeakReference que recuerde su ID, de modo que su hilo de limpieza puede eliminar los valores débiles durante la limpieza de los WeakReferences.
La implementación de la referencia débil, con el necesario ReferenceQueue y el hilo de limpieza sería algo como esto:
class CustomObjectAccess { private static final ReferenceQueue<CustomObject> releasedCustomObjects = new ReferenceQueue<>(); static { Thread cleanupThread = new Thread("CustomObject cleanup thread") while (true) { CustomObjectWeakReference freed = (CustomObjectWeakReference) CustomObjectWeakReference.releasedCustomObjects.remove(); cache.remove(freed.id); } }; cleanupThread.start(); } private Map<CustomObjectID, CustomObjectWeakReference> cache; public CustomObject get(CustomObjectID id) { synchronized(this){ CustomObject result= getFromCache(id); if (result==null) { result=getObjectFromPersistence(id); addToCache(result); } } return result; } private addToCache(CustomObject co) { cache.put(CustomObject.getID(), new CustomObjectWeakReference(co)); } private getFromCache(CustomObjectID id) { WeakReference<CustomObject> weak = cache.get(id); if (weak != null) { return weak.get(); } return null; } class CustomObjectWeakReference extends WeakReference<CustomObject> { private final CustomObjectID id; CustomObjectWeakReference(CustomObject co) { super(co, releasedCustomObjects); this.id = co.getID(); } } }
- Detectar el estado de dos tarjetas SIM en un teléfono Android de doble SIM
- Almacenamiento de dimensiones en un archivo xml en Android