recorrer - Iteraciones sobre las colecciones de Java en Scala
recorrer lista con lambda (9)
A partir de Scala 2.8, todo lo que tiene que hacer es importar el objeto JavaConversions, que ya declara las conversiones adecuadas.
import scala.collection.JavaConversions._
Sin embargo, esto no funcionará en versiones anteriores.
Estoy escribiendo un código de Scala que utiliza la API de POI de Apache . Me gustaría iterar sobre las filas contenidas en el java.util.Iterator
que obtengo de la clase Sheet. Me gustaría utilizar el iterador en a for each
ciclo de estilo, por lo que he intentado convertirlo a una colección Scala nativa, pero no habrá suerte.
He examinado las características / características de la envoltura de Scala, pero no veo cómo usarlas correctamente. ¿Cómo iterar sobre una colección de Java en Scala sin usar el estilo verboso while(hasNext()) getNext()
del ciclo?
Aquí está el código que escribí basado en la respuesta correcta:
class IteratorWrapper[A](iter:java.util.Iterator[A])
{
def foreach(f: A => Unit): Unit = {
while(iter.hasNext){
f(iter.next)
}
}
}
object SpreadsheetParser extends Application
{
implicit def iteratorToWrapper[T](iter:java.util.Iterator[T]):IteratorWrapper[T] = new IteratorWrapper[T](iter)
override def main(args:Array[String]):Unit =
{
val ios = new FileInputStream("assets/data.xls")
val workbook = new HSSFWorkbook(ios)
var sheet = workbook.getSheetAt(0)
var rows = sheet.rowIterator()
for (val row <- rows){
println(row)
}
}
}
Con Scala 2.10.4+ (y posiblemente antes), es posible convertir implícitamente java.util.Iterator [A] en scala.collection.Iterator [A] mediante la importación de scala.collection.JavaConversions.asScalaIterator. Aquí hay un ejemplo:
object SpreadSheetParser2 extends App {
import org.apache.poi.hssf.usermodel.HSSFWorkbook
import java.io.FileInputStream
import scala.collection.JavaConversions.asScalaIterator
val ios = new FileInputStream("data.xls")
val workbook = new HSSFWorkbook(ios)
var sheet = workbook.getSheetAt(0)
val rows = sheet.rowIterator()
for (row <- rows) {
val cells = row.cellIterator()
for (cell <- cells) {
print(cell + ",")
}
println
}
}
Hay una clase contenedora ( scala.collection.jcl.MutableIterator.Wrapper
). Entonces si defines
implicit def javaIteratorToScalaIterator[A](it : java.util.Iterator[A]) = new Wrapper(it)
entonces actuará como una subclase del iterador de Scala para que pueda hacer foreach
.
La respuesta correcta aquí es definir una conversión implícita del Iterator
de Java a algún tipo personalizado. Este tipo debe implementar un método foreach
que delegue en el Iterator
subyacente. Esto le permitirá usar un Scala for
-loop con cualquier Java Iterator
.
Para Scala 2.10:
// Feature warning if you don''t enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
Puede convertir la colección de Java en una matriz y usar eso:
val array = java.util.Arrays.asList("one","two","three").toArray
array.foreach(println)
O continúe y convierta la matriz a una lista de Scala:
val list = List.fromArray(array)
Scala 2.12.0 rechaza scala.collection.JavaConversions
, por lo que desde 2.12.0 una forma de hacerlo sería algo como:
import scala.collection.JavaConverters._
// ...
for(k <- javaCollection.asScala) {
// ...
}
(observe la importación, nuevo es JavaConverters, desaprobado es JavaConversions)
Si desea evitar las implícitas en scala.collection.JavaConversions , puede usar scala.collection.JavaConverters para realizar una conversión explícita.
scala> val l = new java.util.LinkedList[Int]()
l: java.util.LinkedList[Int] = []
scala> (1 to 10).foreach(l.add(_))
scala> val i = l.iterator
i: java.util.Iterator[Int] = java.util.LinkedList$ListItr@11eadcba
scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._
scala> i.asScala.mkString
res10: String = 12345678910
Tenga en cuenta el uso del método asScala
para convertir Java Iterator
a Scala Iterator
.
Los JavaConverters han estado disponibles desde Scala 2.8.1.
Si está iterando a través de un gran conjunto de datos, entonces probablemente no quiera cargar toda la colección en la memoria con la conversión implícita de .asScala
. En este caso, un enfoque práctico es implementar el rasgo scala.collection.Iterator
import java.util.{Iterator => JIterator}
def scalaIterator[T](it: JIterator[T]) = new Iterator[T] {
override def hasNext = it.hasNext
override def next() = it.next()
}
val jIterator: Iterator[String] = ... // iterating over a large dataset
scalaIterator(jIterator).take(2).map(_.length).foreach(println) // only first 2 elements are loaded to memory
Tiene un concepto similar pero menos detallado IMO :)