迭代时返回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(): Booleannext()返回一些东西,这段代码将被编译。

    通常它是一个可iterator() Iterable<SomeType>实现(某些集合),但是您可以将iterator()方法作为扩展添加到现有的类中,您也可以在for循环中使用该类。

  • 对于解构声明 ,项目类型应该具有componentN()函数。

     val (x, y, z) = item 

    这里编译器需要itemcomponent1()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文档中找到关于解构的更多信息 。