Created
July 17, 2014 18:52
-
-
Save masahitojp/c391c283d0bbb4548f19 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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