From 8920fd3a2e98a1202ef0ea33b53f073e03cae428 Mon Sep 17 00:00:00 2001 From: "Kenneth J. Shackleton" Date: Sat, 22 Jun 2024 14:34:53 +0100 Subject: [PATCH] Optimistic LRU cache. --- .../cache/benchmarks/LruCacheBenchmark.kt | 6 +- .../com/bloomberg/selekt/SQLConnection.kt | 4 +- .../bloomberg/selekt/cache/CommonLruCache.kt | 70 +++++++++++++++++++ .../cache/{LruCache.kt => LinkedLruCache.kt} | 17 ++++- .../selekt/cache/OptimisticLruCache.kt | 51 ++++++++++++++ .../collections/map/FastStampedStringMap.kt | 36 +++++----- .../selekt/collections/map/FastStringMap.kt | 10 +++ ...{LruCacheTest.kt => LinkedLruCacheTest.kt} | 55 ++++++--------- .../map/FastLinkedStringMapTest.kt | 35 ++-------- .../map/FastStampedStringMapTest.kt | 55 ++++++--------- 10 files changed, 216 insertions(+), 123 deletions(-) create mode 100644 selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/CommonLruCache.kt rename selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/{LruCache.kt => LinkedLruCache.kt} (75%) create mode 100644 selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/OptimisticLruCache.kt rename selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/{LruCacheTest.kt => LinkedLruCacheTest.kt} (64%) diff --git a/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/cache/benchmarks/LruCacheBenchmark.kt b/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/cache/benchmarks/LruCacheBenchmark.kt index b07b4fded8..db5d3359cb 100644 --- a/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/cache/benchmarks/LruCacheBenchmark.kt +++ b/selekt-java/src/jmh/kotlin/com/bloomberg/selekt/cache/benchmarks/LruCacheBenchmark.kt @@ -16,7 +16,7 @@ package com.bloomberg.selekt.cache.benchmarks -import com.bloomberg.selekt.cache.LruCache +import com.bloomberg.selekt.cache.LinkedLruCache import org.openjdk.jmh.annotations.Benchmark import org.openjdk.jmh.annotations.BenchmarkMode import org.openjdk.jmh.annotations.Level @@ -27,11 +27,11 @@ import org.openjdk.jmh.annotations.State @State(Scope.Thread) open class CacheInput { - internal lateinit var cache: LruCache + internal lateinit var cache: LinkedLruCache @Setup(Level.Iteration) fun setUp() { - cache = LruCache(1) {} + cache = LinkedLruCache(1) {} } } diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/SQLConnection.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/SQLConnection.kt index ec5f57e62a..c8f76dab42 100644 --- a/selekt-java/src/main/kotlin/com/bloomberg/selekt/SQLConnection.kt +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/SQLConnection.kt @@ -16,7 +16,7 @@ package com.bloomberg.selekt -import com.bloomberg.selekt.cache.LruCache +import com.bloomberg.selekt.cache.LinkedLruCache import com.bloomberg.selekt.commons.forEachByPosition import com.bloomberg.selekt.commons.forUntil import javax.annotation.concurrent.NotThreadSafe @@ -31,7 +31,7 @@ internal class SQLConnection( key: Key? ) : CloseableSQLExecutor { private val pointer = sqlite.open(path, flags) - private val preparedStatements = LruCache(configuration.maxSqlCacheSize) { + private val preparedStatements = LinkedLruCache(configuration.maxSqlCacheSize) { it.close() pooledPreparedStatement = it } diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/CommonLruCache.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/CommonLruCache.kt new file mode 100644 index 0000000000..7ce95e1351 --- /dev/null +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/CommonLruCache.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Bloomberg Finance L.P. + * + * 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 + * + * https://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 com.bloomberg.selekt.cache + +class CommonLruCache( + @PublishedApi + internal val maxSize: Int, + disposal: (T) -> Unit +) { + @PublishedApi + internal var cache: Any = OptimisticLruCache(maxSize, disposal) + + fun evict(key: String) { + when (val cache = cache) { + is OptimisticLruCache<*> -> cache.evict(key) + is LinkedLruCache<*> -> cache.evict(key) + else -> error("Unrecognized cache class: {}") + } + } + + fun evictAll() { + when (val cache = cache) { + is OptimisticLruCache<*> -> cache.evictAll() + is LinkedLruCache<*> -> cache.evictAll() + else -> error("Unrecognized cache class: {}") + } + } + + @Suppress("UNCHECKED_CAST") + inline fun get(key: String, supplier: () -> T): T = when (cache) { + is OptimisticLruCache<*> -> (cache as OptimisticLruCache).let { + if (it.store.size >= maxSize) { + // Adding an entry to the cache will necessitate the removal of the least recently + // used entry first to honour our maximum size constraint. For the implementation + // of the store currently assigned, this is an O(N) operation. We transform to an + // O(1) implementation. + (transform() as LinkedLruCache).get(key, supplier) + } else { + it.get(key, supplier) + } + } + is LinkedLruCache<*> -> (cache as LinkedLruCache).get(key, supplier) + else -> error("Unrecognized cache class: {}") + } + + fun containsKey(key: String) = when (val cache = cache) { + is OptimisticLruCache<*> -> cache.containsKey(key) + is LinkedLruCache<*> -> cache.containsKey(key) + else -> error("Unrecognized cache class: {}") + } + + @PublishedApi + internal fun transform() = (cache as OptimisticLruCache<*>).asLruCache().also { + cache = it + } +} diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LruCache.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LinkedLruCache.kt similarity index 75% rename from selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LruCache.kt rename to selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LinkedLruCache.kt index dcc8e47a51..4c8ccf0110 100644 --- a/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LruCache.kt +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/LinkedLruCache.kt @@ -20,11 +20,22 @@ import com.bloomberg.selekt.collections.map.FastLinkedStringMap import javax.annotation.concurrent.NotThreadSafe @NotThreadSafe -class LruCache(maxSize: Int, disposal: (T) -> Unit) { +class LinkedLruCache( @PublishedApi @JvmField - @JvmSynthetic - internal val store = FastLinkedStringMap(maxSize, maxSize, false, disposal) + internal val maxSize: Int, + @PublishedApi + @JvmField + internal val store: FastLinkedStringMap +) { + constructor( + maxSize: Int, + disposal: (T) -> Unit + ) : this(maxSize, FastLinkedStringMap( + maxSize = maxSize, + disposal = disposal, + accessOrder = true + )) fun evict(key: String) { store.removeKey(key) diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/OptimisticLruCache.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/OptimisticLruCache.kt new file mode 100644 index 0000000000..8e10f3dde4 --- /dev/null +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/cache/OptimisticLruCache.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2024 Bloomberg Finance L.P. + * + * 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 + * + * https://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 com.bloomberg.selekt.cache + +import com.bloomberg.selekt.collections.map.FastStampedStringMap +import javax.annotation.concurrent.NotThreadSafe + +@NotThreadSafe +class OptimisticLruCache( + @PublishedApi + @JvmField + internal val maxSize: Int, + @PublishedApi + @JvmField + internal val disposal: (T) -> Unit +) { + @PublishedApi + @JvmField + internal var store = FastStampedStringMap(capacity = maxSize, disposal = disposal) + + fun evict(key: String) { + store.removeKey(key) + } + + fun evictAll() { + store.clear() + } + + inline fun get(key: String, supplier: () -> T): T = store.getElsePut(key, supplier) + + fun containsKey(key: String) = store.containsKey(key) + + internal fun asLruCache() = LinkedLruCache( + maxSize = maxSize, + store = store.asLinkedMap(maxSize, disposal) + ) +} diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt index 63d70e7bf6..71c7bca69f 100644 --- a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMap.kt @@ -20,7 +20,8 @@ import javax.annotation.concurrent.NotThreadSafe @NotThreadSafe class FastStampedStringMap( - capacity: Int + capacity: Int, + private val disposal: (T) -> Unit ) : FastStringMap(capacity) { private var currentStamp = Int.MIN_VALUE private var spare: StampedEntry? = null @@ -51,9 +52,16 @@ class FastStampedStringMap( return StampedEntry(index, hashCode, key, value, nextStamp(), store[index]) } + fun removeKey(key: String) { + disposal(super.removeEntry(key).value!!) + } + override fun clear() { - super.clear() + entries().forEach { + disposal(it.value!!) + } spare = null + super.clear() } @PublishedApi @@ -81,27 +89,23 @@ class FastStampedStringMap( } } - private fun entries(): Iterable> = store.flatMap { - sequence { - var current = it - while (current != null) { - yield(current) - current = current.after - } - } - }.asIterable() - private fun resetAllStamps() { - entries().sortedBy { - (it as StampedEntry).stamp - }.run { + @Suppress("UNCHECKED_CAST") + (entries() as Iterable>).sortedBy(StampedEntry::stamp).run { currentStamp = Int.MIN_VALUE + maxOf(0, size - 1) forEachIndexed { index, it -> - (it as StampedEntry).stamp = Int.MIN_VALUE + index + it.stamp = Int.MIN_VALUE + index } } } + @Suppress("UNCHECKED_CAST") + @PublishedApi + internal fun removeLastEntry(): StampedEntry = (entries() as Iterable>) + .minBy(StampedEntry::stamp).let { + (removeEntry(it.key) as StampedEntry).apply { disposal(value!!) } + } + @PublishedApi internal class StampedEntry( index: Int, diff --git a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStringMap.kt b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStringMap.kt index dd5e68492f..cfc7ea8f8c 100644 --- a/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStringMap.kt +++ b/selekt-java/src/main/kotlin/com/bloomberg/selekt/collections/map/FastStringMap.kt @@ -95,6 +95,16 @@ open class FastStringMap(capacity: Int) { value: T ): Entry = Entry(index, hashCode, key, value, store[index]) + internal fun entries(): Iterable> = store.flatMap { + sequence { + var current = it + while (current != null) { + yield(current) + current = current.after + } + } + } + open fun clear() { store.fill(null) size = 0 diff --git a/selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LruCacheTest.kt b/selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LinkedLruCacheTest.kt similarity index 64% rename from selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LruCacheTest.kt rename to selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LinkedLruCacheTest.kt index 5c84815ef2..0102172bba 100644 --- a/selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LruCacheTest.kt +++ b/selekt-java/src/test/kotlin/com/bloomberg/selekt/cache/LinkedLruCacheTest.kt @@ -32,22 +32,23 @@ import kotlin.test.assertSame import kotlin.test.assertTrue import kotlin.test.fail -internal class LruCacheTest { +internal class LinkedLruCacheTest { + private val first = Any() + private val second = Any() + private val supplier = mock<() -> Any>() + private val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } + @Test fun get() { val first = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) cache.get("1") { first } assertSame(first, cache.get("1") { fail() }) } @Test fun getTwo() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(2, disposal) + val cache = LinkedLruCache(2, disposal) cache.get("1") { first } cache.get("2") { second } assertSame(first, cache.get("1") { fail() }) @@ -56,10 +57,7 @@ internal class LruCacheTest { @Test fun getAfterEvict() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) cache.get("1") { first } cache.get("2") { second } assertFalse(cache.containsKey("1")) @@ -68,10 +66,7 @@ internal class LruCacheTest { @Test fun evict() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(2, disposal) + val cache = LinkedLruCache(2, disposal) cache.get("1") { first } cache.get("2") { second } cache.evict("1") @@ -83,10 +78,7 @@ internal class LruCacheTest { @Test fun evictAll() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(2, disposal) + val cache = LinkedLruCache(2, disposal) cache.get("1") { first } cache.get("2") { second } cache.evictAll() @@ -98,8 +90,7 @@ internal class LruCacheTest { @Test fun evictWhenEmpty() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) assertThrows { cache.evict("1") } @@ -108,23 +99,21 @@ internal class LruCacheTest { @Test fun evictLeastRecentlyUsed() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(2, disposal) + val third = Any() cache.get("1") { first } cache.get("2") { second } + cache.get("1") { fail() } + cache.get("3") { third } inOrder(disposal) { - verify(disposal, times(1)).invoke(same(first)) + verify(disposal, times(1)).invoke(same(second)) } } @Test fun getWhenAbsent() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) val item = cache.get("1", supplier) verify(supplier, times(1)).invoke() assertSame(item, cache.get("1", supplier)) @@ -132,20 +121,16 @@ internal class LruCacheTest { @Test fun containsFalse() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) cache.get("1", supplier) assertFalse(cache.containsKey("2")) } @Test fun containsTrue() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val cache = LruCache(1, disposal) + val cache = LinkedLruCache(1, disposal) cache.get("1", supplier) assertTrue(cache.containsKey("1")) } diff --git a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastLinkedStringMapTest.kt b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastLinkedStringMapTest.kt index bf7a6de421..efd8e89869 100644 --- a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastLinkedStringMapTest.kt +++ b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastLinkedStringMapTest.kt @@ -34,19 +34,19 @@ import kotlin.test.assertTrue import kotlin.test.fail internal class FastLinkedStringMapTest { + private val first = Any() + private val second = Any() + private val supplier = mock<() -> Any>() + private val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } + @Test fun get() { - val first = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(1, 1, false, disposal) assertSame(first, map.getElsePut("1") { first }) } @Test fun getTwo() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(2, 64, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -56,9 +56,6 @@ internal class FastLinkedStringMapTest { @Test fun getAfterEvict() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(1, 1, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -68,9 +65,6 @@ internal class FastLinkedStringMapTest { @Test fun remove() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(2, 64, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -83,9 +77,6 @@ internal class FastLinkedStringMapTest { @Test fun clear() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(2, 64, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -98,7 +89,6 @@ internal class FastLinkedStringMapTest { @Test fun removeWhenEmpty() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(1, 1, false, disposal) assertThrows { map.removeKey("1") @@ -108,9 +98,6 @@ internal class FastLinkedStringMapTest { @Test fun removeLastEntryAccessed() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(2, 2, true, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -123,9 +110,6 @@ internal class FastLinkedStringMapTest { @Test fun removeLastEntryInserted() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(2, 2, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -138,9 +122,6 @@ internal class FastLinkedStringMapTest { @Test fun evictLeastRecentlyUsed() { - val first = Any() - val second = Any() - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } val map = FastLinkedStringMap(1, 1, false, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } @@ -151,8 +132,6 @@ internal class FastLinkedStringMapTest { @Test fun getWhenAbsent() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() val map = FastLinkedStringMap(1, 1, false, disposal) val item = map.getElsePut("1", supplier) @@ -162,8 +141,6 @@ internal class FastLinkedStringMapTest { @Test fun containsFalse() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() val map = FastLinkedStringMap(1, 1, false, disposal) map.getElsePut("1", supplier) @@ -172,8 +149,6 @@ internal class FastLinkedStringMapTest { @Test fun containsTrue() { - val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() val map = FastLinkedStringMap(1, 1, false, disposal) map.getElsePut("1", supplier) diff --git a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt index c0cb7fe458..398332d533 100644 --- a/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt +++ b/selekt-java/src/test/kotlin/com/bloomberg/selekt/collections/map/FastStampedStringMapTest.kt @@ -31,34 +31,35 @@ import kotlin.test.assertTrue import kotlin.test.fail internal class FastStampedStringMapTest { + private val first = Any() + private val second = Any() + private val supplier = mock<() -> Any>() + private val disposal: (Any) -> Unit = mock { onGeneric { invoke(it) } doReturn Unit } + @Test fun get() { - val first = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) assertSame(first, map.getElsePut("1") { first }) } @Test fun sizeOne() { - val first = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } assertEquals(1, map.size) } @Test fun getTwice() { - val first = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } assertSame(first, map.getElsePut("1") { fail() }) } @Test fun getWhenAbsent() { - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) val item = map.getElsePut("1", supplier) verify(supplier, times(1)).invoke() assertSame(item, map.getElsePut("1", supplier)) @@ -67,9 +68,7 @@ internal class FastStampedStringMapTest { @Test fun getTwo() { - val first = Any() - val second = Any() - val map = FastStampedStringMap(64) + val map = FastStampedStringMap(64, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } assertEquals(2, map.size) @@ -77,9 +76,7 @@ internal class FastStampedStringMapTest { @Test fun getTwoWithCollisions() { - val first = Any() - val second = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.getElsePut("1") { fail() }) @@ -88,9 +85,7 @@ internal class FastStampedStringMapTest { @Test fun sizeTwo() { - val first = Any() - val second = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.getElsePut("1") { fail() }) @@ -99,17 +94,14 @@ internal class FastStampedStringMapTest { @Test fun removeOne() { - val first = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } assertSame(first, map.removeEntry("1").value) } @Test fun removeTwo() { - val first = Any() - val second = Any() - val map = FastStampedStringMap(2) + val map = FastStampedStringMap(2, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.removeEntry("1").value) @@ -118,9 +110,7 @@ internal class FastStampedStringMapTest { @Test fun removeTwoWithCollisions() { - val first = Any() - val second = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } map.getElsePut("2") { second } assertSame(first, map.removeEntry("1").value) @@ -129,8 +119,7 @@ internal class FastStampedStringMapTest { @Test fun removeThenSize() { - val first = Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { first } map.removeEntry("1") assertEquals(0, map.size) @@ -138,7 +127,7 @@ internal class FastStampedStringMapTest { @Test fun removeWhenEmpty() { - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) assertThrows { map.removeEntry("1") } @@ -147,7 +136,7 @@ internal class FastStampedStringMapTest { @Test fun clear() { - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1") { Any() } assertEquals(1, map.size) map.clear() @@ -156,25 +145,23 @@ internal class FastStampedStringMapTest { @Test fun clearWhenEmpty() { - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.clear() assertTrue(map.isEmpty()) } @Test fun containsFalse() { - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1", supplier) assertFalse(map.containsKey("2")) } @Test fun containsTrue() { - val supplier = mock<() -> Any>() whenever(supplier.invoke()) doReturn Any() - val map = FastStampedStringMap(1) + val map = FastStampedStringMap(1, disposal) map.getElsePut("1", supplier) assertTrue(map.containsKey("1")) }