迭代时返回collection元素的双重索引
在Kotlin文档中,我找到了以下示例:
for ((index, value) in array.withIndex()) { println("the element at $index is $value") }
是否有可能(以及如何)做类似于二维矩阵:
for ((i, j, value) in matrix2D.withIndex()) { // but iterate iver double index: i - row, j - column if (otherMatrix2D[i, j] > value) doSomething() }
如何在Kotlin类中支持这个功能?
虽然由miensol和hotkey提出的解决方案是正确的,但它将是迭代矩阵的最不有效的方式。 例如, 热键的解决方案使得M * N
分配Cell<T>
加上List<Cell<T>>
M
分配和IntRange
加上List<List<Cell<T>>>
和IntRange
一个分配。 此外,当添加新的单元格时,列表会调整大小,以便进行更多的分配。 这只是迭代矩阵太多的分配。
使用内联函数进行迭代
我会建议你实现一个非常相似和非常有效的同时扩展功能,将类似于Array<T>.forEachIndexed
。 这个解决方案根本就没有做任何分配,而且和嵌套循环一样高效。
inline fun <T> Matrix<T>.forEachIndexed(callback: (Int, Int, T) -> Unit) { for (i in 0..cols - 1) { for (j in 0..rows - 1) { callback(i, j, this[i, j]) } } }
您可以通过以下方式调用该函数:
matrix.forEachIndexed { i, j, value -> if (otherMatrix[i, j] > value) doSomething() }
迭代使用破坏性的声明
如果你想使用传统的for
-loop破坏性声明出于某种原因,有一种更有效的方法,但hacky的解决方案。 它使用一个序列,而不是分配多个列表,只创建一个Cell
实例,但Cell
本身是可变的。
data class Cell<T>(var i: Int, var j: Int, var value: T) fun <T> Matrix<T>.withIndex(): Sequence<Cell<T>> { val cell = Cell(0, 0, this[0, 0]) return generateSequence(cell) { cell -> cell.j += 1 if (cell.j >= rows) { cell.j = 0 cell.i += 1 if (cell.i >= cols) { return@generateSequence null } } cell.value = this[cell.i, cell.j] cell } }
你可以使用这个函数在for
循环中遍历一个矩阵:
for ((i, j, item) in matrix.withIndex()) { if (otherMatrix[i, j] > value) doSomething() }
这个解决方案比起第一个解决方案效率稍低,而且由于可变的Cell
而不是很强大,所以我真的推荐你使用第一个。
这两种语言功能用于实现您想要的行为:
-
For循环可以与任何具有提供迭代器的方法的类一起使用。
for (item in myItems) { ... }
如果
myItems
有函数iterator()
用函数hasNext(): Boolean
和next()
返回一些东西,这段代码将被编译。通常它是一个可
iterator()
Iterable<SomeType>
实现(某些集合),但是您可以将iterator()
方法作为扩展添加到现有的类中,您也可以在for循环中使用该类。 -
对于解构声明 ,项目类型应该具有
componentN()
函数。val (x, y, z) = item
这里编译器需要
item
有component1()
,component2()
和component3()
函数。 你也可以使用data
类 ,他们有这些功能生成。for循环中的解构以类似的方式工作:迭代器的
next()
返回的类型必须具有componentN()
函数。
示例实现(不要假装在性能上最好,见下文):
-
具有解构支持的类:
class Cell<T>(val i: Int, val j: Int, val item: T) { operator fun component1() = i operator fun component2() = j operator fun component3() = item }
或者使用
data
类:data class Cell<T>(val i: Int, val j: Int, val item: T)
-
返回
List<Cell<T>>
函数(写成扩展名,但也可以是成员函数):fun <T> Matrix<T>.withIndex() = (0 .. height - 1).flatMap { i -> (0 .. width - 1). map { j -> Cell(i, j, this[i, j]) } }
-
用法:
for ((i, j, item) in matrix2d.withIndex()) { ... }
由迈克尔提供的UPD解决方案实际上表现更好(运行这个测试 ,差异大约是2倍到3倍),所以它更适合性能严重的代码。
以下方法:
data class Matrix2DValue<T>(val x: Int, val y: Int, val value: T) fun withIndex(): Iterable<Matrix2DValue<T>> { //build the list of values }
将允许你写for
:
for ((x, y, value) in matrix2d.withIndex()) { println("value: $value, x: $x, y: $y") }
请记住,声明data class
属性的顺序定义了(x, y, value)
– 而不是变量名。 您可以在Kotlin文档中找到关于解构的更多信息 。