Kotlin - 在索引范围内的 IntArray 中查找最小值

Kotlin - Find minimum value in an IntArray within range of indices

我正在将一些较旧的 Java 代码重构到 Kotlin。有一个函数 returns Kotlin IntArray 中的元素在 [a, b] 范围内保持的最小值的索引。范围值默认为 0,数组大小为 - 1。

我想做一些类似...

return data.minOf().indexOf()

...但仅在 dataab 索引之间迭代。

函数如下:

// data is the IntArray property that I'm looping through. 
fun absMinIndex(a: Int = 0, b: Int = (data.size - 1)) : Int {
    var minVal = data[a]
    var minIndex = 0

    for (i in (a + 1)..b) {
        val e = data[i]
        if (e < minVal) {
            minVal = e
            minIndex = i
        }
    }
    return maxIndex
}

这个 [for 循环] 通过从不访问超出范围的索引并且不生成副本 array/sub-array 很好地解决了这个问题。我想知道是否可以完成 'prettier'.

问题

是否有更惯用的 Kotlin 方法来迭代范围内的数组,而不会对我当前解决方案的性能产生负面影响?

为清楚起见编辑了一些代码。

正如 Tenfour04 所建议的,在不损失性能的情况下,您无能为力。但是按照你的想法,改变你的调用方式,你可以把它变成一个扩展函数。

fun IntArray.findIndexOfMinInRange(a: Int = 0, b: Int = this.size - 1): Int {
    var maxVal = get(a)
    var maxIndex = 0

    for (i in (a + 1)..b) {
        if (get(i) < maxVal) {
            maxVal = get(i)
            maxIndex = i
        }
    }
    return maxIndex
}

//and call it like this: 

data.findIndexOfMinInRange(0, 15) //or without anything in the parentheses for the default values

我要更改的一件事是函数内的变量名,我们搜索的是最小值,而不是最大值,还有最小值的索引,而不是最大索引。也许(也许很大)创建一个 data[it] 的 val 而不是访问它两次可能会更好(老实说不确定,我们会用 .get 换取几个字节的内存)。

所以总而言之,我可能会把它留在这里:

fun IntArray.findIndexOfMinInRange(fromIndex: Int = 0, toIndex: Int = this.size - 1): Int {
    var min = get(fromIndex)
    var indexOfMin = fromIndex

    for(i in (fromIndex + 1)..toIndex){
        val current = get(i)
        if (current < min) {
            min = current
            indexOfMin = i
        }
    }

    return indexOfMin
}

//would be called in the same way the one above

另外,请注意,如果您创建了一个特定大小的 IntArray,并且没有完全填充它,它将为未填充的默认值保留 0.如果你这样做:

val data = IntArray(6)
data[0] = 10
data[1] = 11
data[2] = 100
data[3] = 9
data[4] = 50

那么实际的数组就是[10, 11, 100, 9, 50, 0].

我相信这种方法会更地道:

  1. 使用IntRange作为输入参数
  2. IntArray 定义扩展方法,提供自定义迭代器以遍历所需范围内的列表,将值包装到 IndexedValue:
fun IntArray.withIndexInRange(range: IntRange = 0..lastIndex) = Iterable {
    require(range.first >= 0 && range.last <= lastIndex)
    object : Iterator<IndexedValue<Int>> {
        private var index = range.first
        override fun hasNext() = index <= range.last
        override fun next() = IndexedValue(index, this@withIndexInRange[index++])
    }
}
  1. 使用 stdlib 中的 minByOrNull 方法找到最小值或为方便起见将其包装到另一个扩展方法中:
fun <T : Comparable<T>> Iterable<IndexedValue<T>>.indexOfMinOrNull() = minByOrNull { it.value }?.index

用法:

data.withIndexInRange(a..b).indexOfMinOrNull()

请注意,这会有一些性能损失(N 个额外对象的创建和 GC),但正如 Donald Knuth 所说:

Premature optimization is the root of all evil

所以,我相信更好的可读性是值得的。