如何切片变量参数



我编写了一个扩展函数来根据名称获取JSON对象的元素:

fun JSONObject.obj (name: String): JSONObject? =
try { this.getJSONObject(name) }
catch (e: JSONException) { null }

现在我想将其扩展到嵌套JSON对象。我写了以下内容:

tailrec fun JSONObject.obj (first: String, vararg rest: String): JSONObject? =
if (rest.size == 0)
obj(first)
else
obj(first)?.obj(rest[0], *rest.drop(1).toTypedArray())

但是我觉得这看起来效率很低。

切割vararg参数的最佳方法是什么?

只能在公共函数中使用var,然后在内部使用list进行递归:

fun JSONObject.obj (first: String, vararg rest: String): JSONObject? = obj(first, rest.asList())
private tailrec fun JSONObject.obj (first: String, rest: List<String>): JSONObject? =
if (rest.size == 0)
obj(first)
else
obj(first)?.obj(rest[0], rest.subList(1, rest.size))

asList()subList()都不复制数据,而只是包装现有的集合。尽管如此,这还远远不够理想,因为它为每次迭代创建一个新对象,并且可能创建一个视图链(这取决于subList()的内部实现)。或者,内部函数可以接收一个数组和偏移量——这将解决上述两个问题。

一般来说,我建议不要尝试将Kotlin变成它不是的东西。它对函数式结构的支持有限,但它不是函数式语言。如果没有链表的实现(可以很容易地分成头和尾),这种风格的代码将总是低效和/或麻烦。您可以寻找这样的实现,例如Arrow或kotlinx.collections.immutable。后者具有ImmutableList和优化的subList() -您可以将其与上面提供的解决方案一起使用,以避免创建列表链。

事实上,Java标准库中的基本列表实现也提供了优化的subList(): AbstractList.java。因此,上面的解决方案只使用asList()应该是可以的,至少在针对JVM时是这样。

不是切片,为什么不尝试迭代所有对象并获得JSONObjects?我认为这样会更有效率。

fun JSONObject.obj(vararg names: String): JSONObject? {
var jsonObject = this
for (name in names) {
if (!jsonObject.has(name))
return null
jsonObject = jsonObject.getJSONObject(name)
}
return jsonObject
}

最新更新