Sunday, 12 February 2012

Concurrent map with timed out elements using Guava library

Sometimes we have a need to use a map, in which elements live only for a specified amount of time.
We could implement such map ourselves (by overriding removeEldestEntry() from LinkedHashMap or using thread that will be removing elements periodically) but the better idea is to use already existing implementation. Google's guava library (http://code.google.com/p/guava-libraries) is the solution for us.
Let's write some simple unit tests to check the API and verify behaviour:
public class ConcurrentMapExampleTest {
private static final int TIMEOUT_IN_MILLIS = 500;
private static final int KEY = 1;
private static final String VALUE = "David de Gea";
private ConcurrentMap<Integer, String> concurrentMap;
@Before
public void setUp() {
Cache<Integer, String> cache = CacheBuilder.newBuilder().expireAfterWrite(TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS).build();
concurrentMap = cache.asMap();
}
@Test
public void shouldRemoveElementWhenTimeoutExpires() throws InterruptedException {
// when
concurrentMap.put(KEY, VALUE);
Thread.sleep(TIMEOUT_IN_MILLIS * 2); // wait for the element to be removed
// then
assertThat(concurrentMap.containsKey(KEY), is(false));
assertThat(concurrentMap.containsValue(VALUE), is(false));
}
@Test
public void shouldNotRemoveElementWhenTimeoutDoesNotExpire() throws InterruptedException {
// when
concurrentMap.put(KEY, VALUE);
// then
assertThat(concurrentMap.containsKey(KEY), is(true));
assertThat(concurrentMap.containsValue(VALUE), is(true));
}
}
Map is created using the builder pattern. Apart from specifying the time of elements expiration, it allows among others, setting the maximum map's size or the listener that will be notified each time an entry is removed. As the built object is an instance of Cache, we need to create ConcurrentMap from it using asMap() method.
Two tests verify that added element is removed only when the timeout expires.

1 comment: