Skip to content

Commit

Permalink
Make cache interface generic (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
abdurahmanadilovic authored May 5, 2020
1 parent 813c976 commit caadbc5
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 56 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,12 @@ We are given a `flushInterval`, and we will clear the cache every `flushInterval

Besides the three implementations we discussed above, here are several implementations such as `FIFOCache`, `SoftCache` and `WeakCache`, implemented with **[First-in-first-out algorithm](https://en.wikipedia.org/wiki/FIFO_%28computing_and_electronics%29)**, **[Soft Reference](https://en.wikipedia.org/wiki/Soft_reference)**, and **[Weak Reference](https://en.wikipedia.org/wiki/Weak_reference)** respectively.

## None-typed Cache

To make a cache more flexible, in terms of values it can store, Cache type can be used which is just a type-alias

```kotlin
typealias Cache = GenericCache<Any, Any>
```

You can check out the source code [here in GitHub](https://github.com/kezhenxu94/cache-lite).
29 changes: 2 additions & 27 deletions src/main/kotlin/io/github/kezhenxu94/cache/lite/Cache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,6 @@
package io.github.kezhenxu94.cache.lite

/**
* [Cache] defines the basic operations to a cache.
* Non typed [Cache] alias that uses [GenericCache]
*/
interface Cache {
/**
* The number of the items that are currently cached.
*/
val size: Int

/**
* Cache a [value] with a given [key]
*/
operator fun set(key: Any, value: Any)

/**
* Get the cached value of a given [key], or null if it's not cached or evicted.
*/
operator fun get(key: Any): Any?

/**
* Remove the value of the [key] from the cache, and return the removed value, or null if it's not cached at all.
*/
fun remove(key: Any): Any?

/**
* Remove all the items in the cache.
*/
fun clear()
}
typealias Cache = GenericCache<Any, Any>
47 changes: 47 additions & 0 deletions src/main/kotlin/io/github/kezhenxu94/cache/lite/GenericCache.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2020 kezhenxu94
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.github.kezhenxu94.cache.lite

/**
* A Generic K,V [GenericCache] defines the basic operations to a cache.
*/
interface GenericCache<K, V> {
/**
* The number of the items that are currently cached.
*/
val size: Int

/**
* Cache a [value] with a given [key]
*/
operator fun set(key: K, value: V)

/**
* Get the cached value of a given [key], or null if it's not cached or evicted.
*/
operator fun get(key: K): V?

/**
* Remove the value of the [key] from the cache, and return the removed value, or null if it's not cached at all.
*/
fun remove(key: K): V?

/**
* Remove all the items in the cache.
*/
fun clear()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

package io.github.kezhenxu94.cache.lite.impl

import io.github.kezhenxu94.cache.lite.Cache
import io.github.kezhenxu94.cache.lite.GenericCache
import java.util.concurrent.TimeUnit

/**
* [ExpirableCache] flushes the items whose life time is longer than [flushInterval].
*/
class ExpirableCache(private val delegate: Cache,
private val flushInterval: Long = TimeUnit.MINUTES.toMillis(1)) : Cache by delegate {
class ExpirableCache<K, V>(
private val delegate: GenericCache<K, V>,
private val flushInterval: Long = TimeUnit.MINUTES.toMillis(1)
) : GenericCache<K, V> by delegate {
private var lastFlushTime = System.nanoTime()

override val size: Int
Expand All @@ -32,12 +34,12 @@ class ExpirableCache(private val delegate: Cache,
return delegate.size
}

override fun remove(key: Any): Any? {
override fun remove(key: K): V? {
recycle()
return delegate.remove(key)
}

override fun get(key: Any): Any? {
override fun get(key: K): V? {
recycle()
return delegate[key]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,29 @@

package io.github.kezhenxu94.cache.lite.impl

import io.github.kezhenxu94.cache.lite.Cache
import io.github.kezhenxu94.cache.lite.GenericCache

/**
* [FIFOCache] caches at most [minimalSize] items that are recently [set].
*/
class FIFOCache(private val delegate: Cache, private val minimalSize: Int = DEFAULT_SIZE) : Cache by delegate {
private val keyMap = object : LinkedHashMap<Any, Any>(minimalSize, .75f) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<Any, Any>): Boolean {
class FIFOCache<K, V>(private val delegate: GenericCache<K, V>, private val minimalSize: Int = DEFAULT_SIZE) :
GenericCache<K, V> by delegate {
private val keyMap = object : LinkedHashMap<K, Boolean>(minimalSize, .75f) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, Boolean>): Boolean {
val tooManyCachedItems = size > minimalSize
if (tooManyCachedItems) eldestKeyToRemove = eldest.key
return tooManyCachedItems
}
}

private var eldestKeyToRemove: Any? = null
private var eldestKeyToRemove: K? = null

override fun set(key: Any, value: Any) {
override fun set(key: K, value: V) {
delegate[key] = value
cycleKeyMap(key)
}

override fun get(key: Any): Any? {
override fun get(key: K): V? {
keyMap[key]
return delegate[key]
}
Expand All @@ -47,7 +48,7 @@ class FIFOCache(private val delegate: Cache, private val minimalSize: Int = DEFA
delegate.clear()
}

private fun cycleKeyMap(key: Any) {
private fun cycleKeyMap(key: K) {
keyMap[key] = PRESENT
eldestKeyToRemove?.let { delegate.remove(it) }
eldestKeyToRemove = null
Expand Down
16 changes: 8 additions & 8 deletions src/main/kotlin/io/github/kezhenxu94/cache/lite/impl/LRUCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,28 @@

package io.github.kezhenxu94.cache.lite.impl

import io.github.kezhenxu94.cache.lite.Cache
import io.github.kezhenxu94.cache.lite.GenericCache

/**
* [LRUCache] flushes items that are **Least Recently Used** and keeps [minimalSize] items at most.
*/
class LRUCache(private val delegate: Cache, private val minimalSize: Int = DEFAULT_SIZE) : Cache by delegate {
private val keyMap = object : LinkedHashMap<Any, Any>(minimalSize, .75f, true) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<Any, Any>): Boolean {
class LRUCache<K, V>(private val delegate: GenericCache<K, V>, private val minimalSize: Int = DEFAULT_SIZE) : GenericCache<K, V> by delegate {
private val keyMap = object : LinkedHashMap<K, Boolean>(minimalSize, .75f, true) {
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, Boolean>): Boolean {
val tooManyCachedItems = size > minimalSize
if (tooManyCachedItems) eldestKeyToRemove = eldest.key
return tooManyCachedItems
}
}

private var eldestKeyToRemove: Any? = null
private var eldestKeyToRemove: K? = null

override fun set(key: Any, value: Any) {
override fun set(key: K, value: V) {
delegate[key] = value
cycleKeyMap(key)
}

override fun get(key: Any): Any? {
override fun get(key: K): V? {
keyMap[key]
return delegate[key]
}
Expand All @@ -47,7 +47,7 @@ class LRUCache(private val delegate: Cache, private val minimalSize: Int = DEFAU
delegate.clear()
}

private fun cycleKeyMap(key: Any) {
private fun cycleKeyMap(key: K) {
keyMap[key] = PRESENT
eldestKeyToRemove?.let { delegate.remove(it) }
eldestKeyToRemove = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@

package io.github.kezhenxu94.cache.lite.impl

import io.github.kezhenxu94.cache.lite.Cache
import io.github.kezhenxu94.cache.lite.GenericCache

/**
* [PerpetualCache] caches the items perpetually unless they're manually [remove]ed.
*/
class PerpetualCache : Cache {
private val cache = HashMap<Any, Any>()
class PerpetualCache<K, V> : GenericCache<K, V> {
private val cache = HashMap<K, V>()

override val size: Int
get() = cache.size

override fun set(key: Any, value: Any) {
override fun set(key: K, value: V) {
cache[key] = value
}

override fun remove(key: Any) = cache.remove(key)
override fun remove(key: K) = cache.remove(key)

override fun get(key: Any) = cache[key]
override fun get(key: K) = cache[key]

override fun clear() = cache.clear()
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package io.github.kezhenxu94.cache.lite.impl

import io.github.kezhenxu94.cache.lite.Cache

import java.lang.ref.ReferenceQueue
import java.lang.ref.SoftReference

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import org.junit.Before
import org.junit.Test

internal abstract class BaseCacheTest {
protected lateinit var cache: Cache
protected lateinit var cache: GenericCache<Any, Any>

@Before
fun setup() {
Expand Down

0 comments on commit caadbc5

Please sign in to comment.