I have a bunch of lookup tables indexed by key
that I would like instantiate lazily (i.e. the tables are expensive to compute and I only expect some of them to be used on any given execution of the code).
private var myLazyMap: Map<KeyClass, TableClass> by lazy { ...}
Doesn't work as that makes the map object itself lazy, which isn't right. I think I may need to write a custom delegate, but I still can't see how to embed that into the map object.
I could wrap TableClass
with something like
class LazyTable(val param: TableClassParameter) {
private var table: TableClass by lazy { TableClass(param) }
fun wrappedTableFun(): ResultClass {
return table.tableFun()
}
}
But this does mean the class is wrong and it feels like a hack. Can this be done in a neater way?
CodePudding user response:
It could be implemented in multiple ways, depending on your needs. Probably the easiest is to use a map of lazy values directly:
val map = mutableMapOf<KeyClass, Lazy<TableClass>>()
map[myKey1] = lazy { createTable1() }
map[myKey2] = lazy { createTable2() }
val table = map[myKey1]?.value
If we want to not expose Lazy
to the users of the map, we need to create our own LazyMap
. One way is to use a map similar to above and just hide Lazy
from the user:
class LazyMap<K, V>(
private val map: Map<K, Lazy<V>>
) {
operator fun get(key: K): V? = map[key]?.value
}
Another solution is to use a function that creates values when needed:
class LazyMap<K, V>(
private val compute: (K) -> V
) {
private val map = mutableMapOf<K, V>()
operator fun get(key: K): V? = map.getOrPut(key) { compute(key) }
}
We can also use a separate compute
function per each key, as in the answer by @tibtof .
CodePudding user response:
A partial implementation could be something like this:
class LazyMap<K, V>(val lazyVals: Map<K, () -> V>, val cache: MutableMap<K, V> = mutableMapOf()) : Map<K, V> by cache {
companion object {
fun <K, V> lazyMapOf(vararg entries: Pair<K, () -> V>): LazyMap<K, V> = LazyMap(mapOf(*entries))
}
override fun get(key: K): V? = cache[key] ?: lazyVals[key]?.let { cache[key] = it(); cache[key] }
}
fun main() {
val lazyMap = lazyMapOf(
1 to { println("computing one"); "one" },
2 to { println("computing two"); "two" }
)
println("get 2")
println(lazyMap[2])
println("get 1")
println(lazyMap[1])
println("get 0")
println(lazyMap[0])
println("get 2 again")
println(lazyMap[2])
}
An we can observe the lazyness in the output:
get 2
computing two
two
get 1
computing one
one
get 0
null
get 2 again
two