Kotlin中惯用的方式来转换/附加到列表?

时间:2019-06-25 16:24:48

标签: kotlin

说我正在尝试实现一些功能:

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
}

3 个答案:

答案 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)

(runnable sample)

它在接收者列表上调用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 }

(runnable sample)

函数getOrNull(index)返回指定索引处的项目;如果索引超出范围,则返回null。后者由?: index处理。


或者,重写您的解决方案,并在函数实现中使用可变列表,将各项附加到该实现中,然后将其作为只读List<Int>返回:

fun List<Int>.padWithIndices(newLength: Int): List<Int> =
    toMutableList().apply {
        addAll(size until newLength)
    }

(runnable sample)

答案 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}}。