Iteraciones sobre las colecciones de Java en Scala

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

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 .

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.

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 

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)

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

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) 

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 🙂

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.