---
title: A simple timed cache in Kotlin
---
###### tags: `Kotlin`
# A simple timed cache in Kotlin
```kotlin
import org.slf4j.Logger
import org.slf4j.helpers.NOPLogger
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.MILLISECONDS
interface Cache {
fun get(key: String): String?
fun put(key: String, value: String)
}
class TimedCache(var log: Logger = NOPLogger.NOP_LOGGER) : Cache {
private var cacheTimeValidityInMillis: Long = 0
private val hashMap = ConcurrentHashMap<String, TimedEntry>()
companion object {
fun expiringEvery(duration: Long, timeUnit: TimeUnit) =
TimedCache().apply {
cacheTimeValidityInMillis = MILLISECONDS.convert(duration, timeUnit)
}
}
override fun get(key: String): String? {
val timedEntry = hashMap[key]
if (timedEntry == null || timedEntry.isExpired()) {
log.debug("cache miss for key $key")
return null
}
return timedEntry.value
}
override fun put(key: String, value: String) {
log.debug("caching $key with value $value")
hashMap[key] = TimedEntry(value, cacheTimeValidityInMillis)
}
data class TimedEntry(val value: String, val maxDurationInMillis: Long) {
private val creationTime: Long = now()
fun isExpired() = (now() - creationTime) > maxDurationInMillis
private fun now() = System.currentTimeMillis()
}
}
```
## Tests
```kotlin
import org.junit.jupiter.api.Test
import shouldBe
import java.util.concurrent.TimeUnit.SECONDS
class TimedCacheTest {
private val timedCache = TimedCache.expiringEvery(1, SECONDS)
@Test
fun `before the cache expiration the cached value is still available`() {
timedCache.put("someKey", "someValue")
Thread.sleep(ONE_SECOND / 2)
timedCache.get("someKey") shouldBe "someValue"
}
@Test
fun `after the cache expiration the cached value is no more available`() {
timedCache.put("someKey", "someValue")
Thread.sleep(ONE_SECOND + 1)
timedCache.get("someKey") shouldBe null
}
@Test
fun `putting again a value on an expired cache entry renews its time validity`() {
timedCache.put("someKey", "someValue")
Thread.sleep(ONE_SECOND + 1)
timedCache.put("someKey", "anotherValue")
timedCache.get("someKey") shouldBe "anotherValue"
}
companion object {
private const val ONE_SECOND: Long = 1000
}
}
```
## A util function
```kotlin
import org.assertj.core.api.Assertions.assertThat
internal infix fun Any?.shouldBe(other: Any?) {
assertThat(this).isEqualTo(other)
}
```