Kotlin:如何从具有索引的特定位置遍历集合(跳过具有索引的N个元素)

时间:2019-05-21 18:00:04

标签: kotlin

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

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

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

我尝试使用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")
}

此解决方案有效,但我希望有一些方法可以避免引入单独的fixedIndex变量并手动处理此问题。

2 个答案:

答案 0 :(得分:3)

您的原始尝试非常接近,只需稍作更改即可使其生效。颠倒withIndex()drop(N)的调用,将withIndex放在第一位。

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

for ((index, item) in inputData.asSequence().withIndex().drop(startFrom)) { ... }    

测试代码:

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")
}

输出:

  

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

就是这样!该答案的其余部分仅为您提供替代方案,包括在最后创建自己的扩展功能的更有效和Kotlinesque解决方案。


如果可以接受收藏集的副本,则可以执行以下简短版本。 withIndex不会导致复制,但是drop(N)会导致复制。

for ((index, item) in inputData.withIndex().drop(startFrom)) { ... }

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

您也可以使用功能性forEach代替for循环。

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

这将带来最佳,最有效的选择。只需在使用ArrayList时编写扩展函数,就可以避免使用包装器类进行延迟评估也没有任何复制。只是一个循环,使用索引和值调用您的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])
    }
}

这可以简单地用于任何非原始数组或列表:

sampleData.forEachIndexed(startFrom) { index, item ->
    println("[$index] => $item")
}

如果您也想使用withIndex(startFrom)样式方法,也可以执行相同的操作。您可以随时扩展Kotlin来获得想要的东西!

答案 1 :(得分:1)

如果这是您所缺少的,我认为最简单的解决方案是只使用一个有范围的for循环:

val startFrom = arr.size / 2;
for (i in startFrom until arr.size) {
    println(String.format("Index %d value %s", i, arr[i]));
}

如果您希望严格避免使用诸如arr[i]之类的表达式,则可以将当前解决方案更改为使用序列。