Iterando sobre colecciones de Java en Scala


113

Estoy escribiendo un código Scala que usa la API de Apache POI . Me gustaría iterar sobre las filas contenidas en el java.util.Iteratorque obtengo de la clase Hoja. Me gustaría usar el iterador en un for eachbucle de estilo, así que he intentado convertirlo en una colección nativa de Scala, pero no tengo suerte.

He mirado las clases / rasgos de envoltura de Scala, pero no puedo ver cómo usarlas correctamente. ¿Cómo iterar sobre una colección de Java en Scala sin usar el while(hasNext()) getNext()estilo detallado de bucle?

Aquí está el código que escribí según 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)
        }
    }
}

Parece que no puedo incluir la línea "para (val fila <- filas) {" sin que el analizador piense que el carácter '<' es una etiqueta de cierre XML. Las
comillas inversas

Debería poder convertir a IteratirWrapper implícitamente, lo que le ahorrará un poco de sintaxis. Google para conversiones implícitas en Scala.
Daniel Spiewak

Respuestas:


28

Hay una clase contenedora ( scala.collection.jcl.MutableIterator.Wrapper). Así que si defines

implicit def javaIteratorToScalaIterator[A](it : java.util.Iterator[A]) = new Wrapper(it)

entonces actuará como una subclase del iterador Scala para que pueda hacerlo foreach.


Debe leer: scala.collection.jcl.MutableIterator.Wrapper
samg

37
Esta respuesta está obsoleta en Scala 2.8; ver stackoverflow.com/questions/2708990/…
Alex R

256

A partir de Scala 2.8, todo lo que tiene que hacer es importar el objeto JavaConversions, que ya declara las conversiones apropiadas.

import scala.collection.JavaConversions._

Sin embargo, esto no funcionará en versiones anteriores.


24

Editar : Scala 2.13.0 está obsoleto scala.collection.JavaConverters, por lo que desde 2.13.0 debe usar scala.jdk.CollectionConverters.

Scala 2.12.0 está en desuso scala.collection.JavaConversions, por lo que desde 2.12.0 una forma de hacer esto sería algo como:

import scala.collection.JavaConverters._

// ...

for(k <- javaCollection.asScala) {
    // ...
}

(observe la importación, nuevo es JavaConverters, obsoleto es JavaConversions)


2
Estaba buscando "toScala" ^ _ ^!
Profiterole

15

La respuesta correcta aquí es definir una conversión implícita de Java Iteratora algún tipo personalizado. Este tipo debería implementar un foreachmétodo que delegue al subyacente Iterator. Esto le permitirá utilizar un forbucle de Scala con cualquier Java Iterator.


9

Para Scala 2.10:

// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator

5

Con Scala 2.10.4+ (y posiblemente anterior) es posible convertir implícitamente java.util.Iterator [A] en scala.collection.Iterator [A] importando 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
  }

}

4

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 en una lista Scala:

val list = List.fromArray(array)

4

Si está iterando a través de un conjunto de datos grande, probablemente no desee cargar la colección completa en la memoria con .asScalaconversión implícita. En este caso, un enfoque práctico es implementar el scala.collection.Iteratorrasgo

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 en mi opinión :)


2

Si desea evitar las implicaciones en scala.collection.JavaConversions , puede usar scala.collection.JavaConverters para convertir explícitamente.

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 asScalamétodo para convertir Java Iteratora Scala Iterator.

Los JavaConverters están disponibles desde Scala 2.8.1.


Solía import scala.collection.JavaConverters._ y después javaList.iterator().asScala de su ejemplo y funcionó
kosiara - Bartosz Kosarzycki
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.