说我正在尝试实现一些功能:
private fun List<Int>.padWithIndices(newLength: Int): List<Int>
它获取一个列表并返回一个带有附加值的列表,其中对于每个新值,此[i] = i。
Kotlin为我们提供了很多不错的方法来附加到列表,连接两个不同的列表,创建可变列表等。如此之多,我不知道在这种情况下哪个是最好的,而且我也不知道有任何隐藏的陷阱。这是我最初的基于for的解决方案,是否有更有效/惯用的解决方案?
fun List<Int>.padWithIndices(newLength: Int): List<Int> {
var newList = this
for (x in size until newLength) {
newList += x
}
return newList
}
答案 0 :(得分:4)
虽然您的解决方案可读性强,但它在List<Int>
循环的每次迭代中复制原始for
的所有项目,就像在+=
上复制var newList: List<Int>
一样每次都会创建一个新列表。最坏情况下的计算复杂度为O(n²),可以提高到O(n)。
对于这种特殊情况,当您只想将连续范围添加到列表中时,像这样的单行函数将起作用:
fun List<Int>.padWithIndices(newLength: Int): List<Int> =
plus(size until newLength)
它在接收者列表上调用plus
函数,生成一个新列表,并在其中附加了参数中的项目。
您也可以将plus
称为operator +
,将行替换为this + (size until newLength)
。
对于使用复杂逻辑来计算要追加到列表的项目的用例,可以使用List(n) { ... }
factory function,它接受提供列表项的lambda,从而确保每个列表项仅被计算一次:
fun List<Int>.padWithIndices(newLength: Int): List<Int> =
List(newLength) { index -> getOrNull(index) ?: index }
函数getOrNull(index)
返回指定索引处的项目;如果索引超出范围,则返回null
。后者由?: index
处理。
或者,重写您的解决方案,并在函数实现中使用可变列表,将各项附加到该实现中,然后将其作为只读List<Int>
返回:
fun List<Int>.padWithIndices(newLength: Int): List<Int> =
toMutableList().apply {
addAll(size until newLength)
}
答案 1 :(得分:0)
可以,但是您要在循环中追加到列表,这样可能会触发列表中的重新分配。
这是更有效的方法,因为首先使用正确的大小初始化列表:
fun List<Int>.padWithIndices(newLength: Int): List<Int> = List(newLength) { index ->
if (index < size) {
get(index)
} else {
index
}
}
答案 2 :(得分:0)
单线:
fun List<Int>.padWithIndices(newLength: Int): List<Int> =
toMutableList().apply { addAll(size until newLength) }
将初始列表转换为MutableList<Int>
(由JVM中的ArrayList支持),然后从复制列表的大小创建IntRange
,直到newLength
,调用MutableList<Int>.addAll(Iterable<Int>)
,最后向下转换为{ {1}}。