Thursday, May 3, 2012

SoftReference Cache

This blog is about creating a SoftReference Cache for Java. I created it to use it in my Android Applications, but it will probably work in any Java environment.

SoftReference

What is a SoftReference? A SoftReference is a java class that represents a reference that me be deleted by the Garbage Collector (GC).

What does this mean? Every object in Java has references to other objects. As long as a object has references to it, the GC will not delete the item. When the application runs out of memory and the GC is unable to free more memory by deleting items that has no real reference to it any more, the items with only SoftRefences to it will be deleted. So, basically the items that only have a SoftReference to it will be kept in memory as long as possible until there is no other way to free memory.

Because objects with SoftRefences to it will be kept in memory as long as possible they are perfect for implementing caches.

To create a SoftReference to a object, you can use the following:

SoftReference<String> ref = new SoftReference<String>("Hello world");

To retrieve the data:

String value = ref.get();

if (value == null) {
  // The reference is cleaned up by the Garbage Collector.
  // Initialize the object again.
}

// Use the value (from the SoftReference or from re-initializing.
.......

SoftReferenceCache

How can we create a cache that uses SoftReferences? We will do this with a HashMap and Java Generics.

First we start with a class called SoftReferenceCache that will get two generic parameters. One for the key's and one for the value's. In the constructor we will create the HashMap.

public class SoftReferenceCache<K, V> {
  private final HashMap<K, SoftReference<V>> mCache;

  public SoftReferenceCache() {
    mCache = new HashMap<K, SoftReference<V>>();
  }
}

To add new items to the cache, we will add a put function.

public void put(K key, V value) {
  mCache.put(key, new SoftReference<V>(value));
}


You see that the SoftReference is automaticly created inside the put function and that the types are used that you give the SoftReferenceCache during construction.

To retrieve the value's from the SoftRefenceCache you have to implement a get function.

public V get(K key) {
  V value = null;

  SoftReference<V> reference = mCache.get(key);

  if (reference != null) {
    value = reference.get();
  }

  return value;
}

This function will check if the reference exist and return it's value. The user of the SoftReferenceCache should still check if the returnvalue isn't null. That can happen if the Garbage Collector has removed the object from memory.

Here is a example of how to use this class.

SoftReferenceCache<Integer, Person> mPersonCache = new SoftReferenceCache<Integer, Person>();

mPersonCache.put(0, new Person("Peter");
mPersonCache.put(1, new Person("Jan");
mPersonCahce.put(2, new Person("Kees");

Person p = (Person)mPersonCache.get(1); // This will retrieve the Person object of Jan.

Below if the full listing of the SoftReferenceCache

package com.peerke.cache;

import java.lang.ref.SoftReference;
import java.util.HashMap;

/**
 * SoftRefenceCache
 * @param <K> The type of the key's.
 * @param <V> The type of the value's.
 */
public class SoftReferenceCache<K, V> {
  private final HashMap<K, SoftReference<V>> mCache;

  public SoftReferenceCache() {
    mCache = new HashMap<K, SoftReference<V>>();
  }

  /**
   * Put a new item in the cache. This item can be gone after a GC run.
   * 
   * @param key
   *            The key of the value.
   * @param value
   *            The value to store.
   */
  public void put(K key, V value) {
    mCache.put(key, new SoftReference<V>(value));
  }

  /**
   * Retrieve a value from the cache (if available).
   * 
   * @param key
   *            The key to look for.
   * @return The value if it's found. Return null if the key-value pair is not
   *         stored yet or the GC has removed the value from memory.
   */
  public V get(K key) {
    V value = null;

    SoftReference<V> reference = mCache.get(key);

    if (reference != null) {
      value = reference.get();
    }

    return value;
  }
}

1 comment:

Post a Comment