Kotlin:如何使用索引从特定位置迭代集合(跳过带有索引的 N 个元素)

Hel*_*ren 0 kotlin

我想从特定位置迭代项目集合。假设我们想从中心开始迭代数组的整个右侧部分:

int startFrom = arr.length / 2;
for (int i = startFrom; i < arr.length; i++)
{
    String.format("Index %d value %s", i, arr[i]);
}
Run Code Online (Sandbox Code Playgroud)

在迭代过程中跟踪真实的索引和值非常重要。作为示例,您将实现就地排序算法

我尝试使用 drop().withIndexes() 来执行此操作,但看起来 drop() 创建了一个新集合,并且我丢失了有关真实索引的信息。如果我们创建一个变量并计算适当的索引,则可以手动修复它

val startFrom = inputData.size / 2
for ((i, item) in inputData.drop(startFrom).withIndex()){
    val fixedIndex = i + startFrom
    println("Index $i, fixed index $fixedIndex value $item")
}
Run Code Online (Sandbox Code Playgroud)

这个解决方案有效,但我希望有一些东西可以帮助避免引入单独的固定索引变量并手动处理这个问题。

Jay*_*ard 5

您最初的尝试非常接近,只需稍作更改即可使其发挥作用。withIndex()颠倒和drop(N)放在第一位的调用withIndex

如果您不想复制集合,可以先使用 将其转换为序列asSequence()

for ((index, item) in inputData.asSequence().withIndex().drop(startFrom)) { ... }    
Run Code Online (Sandbox Code Playgroud)

测试代码:

val sampleData = listOf("a", "b", "c", "d", "e", "f")
val startFrom = sampleData.size / 2

for ((index, item) in sampleData.asSequence().withIndex().drop(startFrom)) {
    println("[$index] => $item")
}
Run Code Online (Sandbox Code Playgroud)

输出:

[3] => d
[4] => e
[5] => f

就是这样!这个答案的其余部分只是为您提供了替代方案,包括最后创建您自己的扩展函数的更高效且 Kotlinesque 的解决方案。


如果集合的副本可以接受,您可以制作以下较短的版本。ThewithIndex不会导致复制,但 thedrop(N)会导致复制。

for ((index, item) in inputData.withIndex().drop(startFrom)) { ... }
Run Code Online (Sandbox Code Playgroud)

急切复制序列可能会更快,这取决于集合的大小、运行时环境和 CPU 缓存。

您还可以使用函数forEach代替for循环。

sampleData.asSequence().withIndex().drop(startFrom).forEach { (index, item) ->
    println("[$index] => $item")       
}
Run Code Online (Sandbox Code Playgroud)

然后提出最好、最有效的选择。Array只需在使用or 时编写一个扩展函数,List这样就不会使用包装类进行惰性求值,也不会进行任何复制。只是一个使用索引和值调用 lambda 的循环。以下是添加新变体的两个新扩展forEachIndexed

inline fun <T> Array<T>.forEachIndexed(startFrom: Int, 
                                action: (index: Int, item: T)->Unit) {
    for (i in startFrom until this.size) {
        action(i, this[i])
    }
}

inline fun <T> List<T>.forEachIndexed(startFrom: Int, 
                               action: (index: Int, item: T)->Unit) {
    for (i in startFrom until this.size) {
        action(i, this[i])
    }
}
Run Code Online (Sandbox Code Playgroud)

对于任何非原始数组或列表都可以简单地调用它:

sampleData.forEachIndexed(startFrom) { index, item ->
    println("[$index] => $item")
}
Run Code Online (Sandbox Code Playgroud)

如果您也想要一个withIndex(startFrom)样式方法,您也可以这样做。您始终可以扩展 Kotlin 来获得您想要的东西!