Skip to content

Instantly share code, notes, and snippets.

@masahitojp
Created July 17, 2014 18:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masahitojp/c391c283d0bbb4548f19 to your computer and use it in GitHub Desktop.
Save masahitojp/c391c283d0bbb4548f19 to your computer and use it in GitHub Desktop.
package me.masahito
import com.google.common.cache.CacheBuilder
import java.util.concurrent.TimeUnit
import scala.collection.mutable.ConcurrentMap
import scala.collection.JavaConversions._
/**
*
* @param maximumSize
* @param initialCapacity
* @param concurrencyLevel
* @tparam K
* @tparam V
*/
class Cache[K <: AnyRef, V <: AnyRef](maximumSize: Int = 5,
expireAfterAccessMinutes: Int = 30,
initialCapacity: Int = 16,
concurrencyLevel: Int = 16) {
private val cache: ConcurrentMap[K, LazyWrapper[V]] = {
{
CacheBuilder.newBuilder()
.maximumSize(maximumSize)
.initialCapacity(initialCapacity)
.concurrencyLevel(concurrencyLevel)
.expireAfterAccess(expireAfterAccessMinutes, TimeUnit.MINUTES)
.softValues()
.asInstanceOf[CacheBuilder[K, LazyWrapper[V] ]]
.build[K, LazyWrapper[V]]()
.asMap()
}
}
/**
* Returns true if this key is associated with a value in the cache and false otherwise.
*
* @param key
* @return
*/
def contains(key: K): Boolean = {
cache.contains(key)
}
/**
* Optionally return the value associated with the given key
*
* @param key
* @return
*/
def get(key: K): Option[V] = {
cache.get(key).map(unwrap)
}
/**
* Associate the given key with the given value. Optionally return any value previously associated with the key.
*
* @param key
* @param value
* @return
*/
def put(key: K, value: V): Option[V] = {
cache.put(key, wrap(value)).map(unwrap)
}
/**
* If the given key is already associated with a value, return that value. Otherwise, associate the key with the
* given value and return None.
*
* @param key
* @param value
* @return
*/
def putIfAbsent(key: K, value: V): Option[V] = {
cache.putIfAbsent(key, wrap(value)).map(unwrap)
}
/**
* Get the value associated with the given key. If no value is already associated, then associate the given value
* with the key and use it as the return value.
*
* Like Scala's ConcurrentMap, the value parameter will be lazily evaluated: that is, it'll only be evaluated if
* there wasn't already a value associated with the given key. However, unlike Scala's ConcurrentMap, this method is
* a thread safe (atomic) operation.
*
* @param key
* @param value
* @return
*/
def getOrElseUpdate(key: K, value: => V): V = {
val newWrapper = wrap(value)
// If there was no previous value, we'll end up calling the .value on newWrapper, which will evaluate it for the
// first (and last) time
cache.putIfAbsent(key, newWrapper).getOrElse(newWrapper).value
}
/**
* Remove the key and any associated value from the cache. Optionally return any previously associated value.
*
* @param key
* @return
*/
def remove(key: K): Option[V] = {
cache.remove(key).map(unwrap)
}
/**
* Remove all keys and values from the cache
*/
def clear() {
cache.clear()
}
/**
* Return how many elements are in the cache
*
* @return
*/
def size: Int = {
cache.size
}
private def wrap[T](value: => T): LazyWrapper[T] = {
new LazyWrapper[T](value)
}
private def unwrap[T](lazyWrapper: LazyWrapper[T]): T = {
lazyWrapper.value
}
}
/**
* A wrapper that avoids evaluating the value until explicitly asked for
*
* @param wrapped
* @tparam T
*/
private class LazyWrapper[T](wrapped: => T) {
// Store in a lazy val to make sure the wrapped value is evaluated at most once
lazy val value = wrapped
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment