El API java.util.Collections disponible en el SDK Java® 2 hace sencilla la implementación del caché. Este API proporciona la clase HashMap, que funciona bien para cachear un objeto, y la clase LinkedList, que funciona bien en combinaciones con la clase HashMap para cachear muchos objetos.
Un objeto HashMap es muy similar a un Hashtable y puede ser usado para mantener una copia temporal de resultados generados préviamente. Los objetos mantenidos en el caché HashMap podría, por ejemplo, ser una lista de subastas completadas.
En este caso, los resultados de una consulta JDBC podrían solicitarse cientos de veces en un segundo por personas que están esperando conocer la puja más alta, pero la lista de resultados completa sólo cambia una vez por minuto cuando se ompleta una subasta. Podemos escribir nuestro programa para recuperar los objetos que no han cambiado desde el caché de resultados en vez de solicitar a la base de datos cada vez y obtener un significante aumento de rendimiento.
Este ejemplo de código ejecuta una consulta a la base de datos por cada minuto, y devuelve copias cacheadas para las solicitudes que vienen entre consultas.
import java.util.*;
import java.io.*;
class DBCacheRecord {
Object data;
long time;
public DBCacheRecord(Object results, long when) {
time=when;
data=results;
}
public Object getResults() {
return data;
}
public long getLastModified() {
return time;
}
}
public class DBCache {
Map cache;
public DBCache() {
cache = new HashMap();
}
public Object getDBData(String dbcommand) {
if(!cache.containsKey(dbcommand)) {
synchronized(cache) {
cache.put(dbcommand, readDBData(dbcommand));
}
} else {
if((new Date().getTime() ) -
((DBCacheRecord)cache.get(
dbcommand)).getLastModified()>=1000) {
synchronized(cache) {
cache.put(dbcommand, readDBData(dbcommand));
}
}
}
return ((DBCacheRecord)cache.get(
dbcommand)).getResults();
}
public Object readDBData(String dbcommand) {
/*Insert your JDBC code here For Example:
ResultSet results=stmt.executeQuery(dbcommand);
*/
String results="example results";
return(new DBCacheRecord(results,new
Date().getTime()));
}
public static void main(String args[]) {
DBCache d1=new DBCache();
for(int i=1;i<=20;i++) {
d1.getDBData(
"select count(*) from results where
TO_DATE(results.completed) <=SYSDATE");
}
}
}
Si nuestra máquina tiene una gran cantidad de memoria y sólo un pequeño número de objetos que cachear entonces un creciente HashMap podría no ser un problema. Sin embargo, si estamos intentar cachear muchos objetos entonces podríamos queres sólo mantener los objetos más recientes en el caché proporcionando el mejor uso de la mémoria de la máquina. Podemos combinar un objeto HashMap con un LinkedList para crear un caché llamado "Most Recently Used" (MRU).
Con un caché MRU, podemos situar una restricción sobre los objetos que permanecen en el caché, y por lo tanto, control sobre el tamaño del caché. Hay tres operaciones principales que puede realizar un caché MRU:
El LinkedList proporciona el mecanismo de cola, y las entradas de la LinkedList contienen la clave de los datos en el HashMap. Para añadir una nueva entrada en la parte superior de la lista, se llama al método addFirst.
Este ejemplo de código usa un caché MRU para mantener un caché de ficheros cargados desde disco. Cuando se solicita un fichero, el programa chequea para ver si el fichero está en el caché. Si el fichero no está en el caché, el programa lee el fichero desde el disco y sitúa una copia en el caché al principio de la lista.
Si el fichero está en el caché, el programa compara la fecha de modificación del fichero y la entrada del caché.
import java.util.*;
import java.io.*;
class myFile {
long lastmodified;
String contents;
public myFile(long last, String data) {
lastmodified=last;
contents=data;
}
public long getLastModified() {
return lastmodified;
}
public String getContents() {
return contents;
}
}
public class MRUCache {
Map cache;
LinkedList mrulist;
int cachesize;
public MRUCache(int max) {
cache = new HashMap();
mrulist= new LinkedList();
cachesize=max;
}
public String getFile(String fname) {
if(!cache.containsKey(fname)) {
synchronized(cache) {
if(mrulist.size() >=cachesize) {
cache.remove(mrulist.getLast());
mrulist.removeLast();
}
cache.put(fname, readFile(fname));
mrulist.addFirst(fname);
}
} else {
if((new File(fname).lastModified())>
((myFile)cache.get(fname)).getLastModified()) {
synchronized(cache) {
cache.put(fname, readFile(fname));
}
}
synchronized(cache) {
mrulist.remove(fname);
mrulist.addFirst(fname);
}
}
return ((myFile)cache.get(fname)).getContents();
}
public myFile readFile(String name) {
File f = new File(name);
StringBuffer filecontents= new StringBuffer();
try {
BufferedReader br=new BufferedReader(
new FileReader(f));
String line;
while((line =br.readLine()) != null) {
filecontents.append(line);
}
} catch (FileNotFoundException fnfe){
return (null);
} catch ( IOException ioe) {
return (null);
}
return (new myFile(f.lastModified(),
filecontents.toString()));
}
public void printList() {
for(int i=0;i<mrulist.size();i++) {
System.out.println("item "+i+"="+mrulist.get(i));
}
}
public static void main(String args[]) {
// Number of entries in MRU cache is set to 10
MRUCache h1=new MRUCache(10);
for(int i=1;i<=20;i++) {
// files are stored in a subdirectory called data
h1.getFile("data"+File.separatorChar+i);
}
h1.printList();
}
}