简体   繁体   English

如何在 Kotlin 中实现惰性值的 map

[英]How to implement a map of lazy values in Kotlin

I have a bunch of lookup tables indexed by key that I would like instantiate lazily (ie the tables are expensive to compute and I only expect some of them to be used on any given execution of the code).我有一堆key索引的查找表,我想懒惰地实例化它们(即这些表的计算成本很高,我只希望它们中的一些用于任何给定的代码执行)。

private var myLazyMap: Map<KeyClass, TableClass> by lazy { ...}

Doesn't work as that makes the map object itself lazy, which isn't right.不起作用,因为这会使 map object 本身变得懒惰,这是不对的。 I think I may need to write a custom delegate, but I still can't see how to embed that into the map object.我想我可能需要编写一个自定义委托,但我仍然看不到如何将其嵌入到 map object 中。

I could wrap TableClass with something like可以用类似的东西包装TableClass

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.但这确实意味着 class 是错误的,感觉就像是黑客攻击。 Can this be done in a neater way?这可以以更整洁的方式完成吗?

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:我们可以观察到 output 中的惰性:

get 2
computing two
two
get 1
computing one
one
get 0
null
get 2 again
two

It could be implemented in multiple ways, depending on your needs.它可以通过多种方式实现,具体取决于您的需要。 Probably the easiest is to use a map of lazy values directly:可能最简单的方法是直接使用惰性值的 map:

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 .如果我们不想将Lazy暴露给 map 的用户,我们需要创建自己的LazyMap One way is to use a map similar to above and just hide Lazy from the user:一种方法是使用与上述类似的 map 并仅对用户隐藏Lazy

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:另一种解决方案是使用 function 在需要时创建值:

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.我们还可以为每个键使用单独的compute function,如@tibtof 的回答所示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM