2009-01-30 20 views
100

Estoy escribiendo algunos códigos de Scala que usan la API Apache POI. 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 un loop de estilo for each, por lo que he intentado convertirlo a una colección Scala nativa, pero no habrá suerte.Iteraciones sobre colecciones de Java en Scala

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 detallado de loop de while(hasNext()) getNext()?

Aquí está el código que escribió basándose 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) 
     } 
    } 
} 
+0

me parece que no puede incluir la línea "para (fila val <- rows) {"sin que el analizador piense que el carácter '<' es una etiqueta de cierre de XML? Los backticks no funcionan –

+0

Debería poder convertir implícitamente a IteratirWrapper, ahorrándole un poco de sintaxis. Google para conversiones implícitas en Scala. –

Respuesta

25

Hay una clase de contenedor (scala.collection.jcl.MutableIterator.Wrapper) Así que si se define

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

entonces actuará como una subclase de la Scala iterador para que pueda hacer foreach.

+0

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

+33

Esta respuesta es obsoleta en Scala 2.8; ver http://stackoverflow.com/questions/2708990/whats-the-new-way-to-iterate-over-a-java-map-in-scala-2-8-0 –

15

La respuesta correcta es definir una conversión implícita de Java de Iterator 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.

4

Se podría convertir la colección de Java a una matriz y utilizar esa:

val array = java.util.Arrays.asList("one","two","three").toArray 
array.foreach(println) 

O seguir y convertir la matriz a una lista Scala:

val list = List.fromArray(array) 
237

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._ 

Esto no funcionará en las versiones anteriores.

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] para scala.collection.Iterator [A] por importando scala.collection.JavaConversions.asScalaIterator. Aquí está 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 
    } 

} 
2

Si desea evitar los implícitos en scala.collection.JavaConversions puede utilizar scala.collection.JavaConverters para convertir de forma 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] = [email protected] 

scala> import scala.collection.JavaConverters._ 
import scala.collection.JavaConverters._ 

scala> i.asScala.mkString 
res10: String = 12345678910 

Nota el uso del método asScala para convertir el Java Iterator a un Iterator Scala.

Los JavaConverters han estado disponibles desde Scala 2.8.1.

+0

Utilicé 'import scala.collection .JavaConverters._' y luego 'javaList.iterator(). AsScala' de tu ejemplo y funcionó –

1

Si está iterando a través de un conjunto de datos grande, entonces probablemente no desea cargar toda la colección en la memoria con la conversión implícita .asScala.En este caso, un enfoque de manera práctica es implementar scala.collection.Iterator rasgo

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 

Ha concepto similar pero menos detallado OMI :)

4

Scala 2.12.0 desaprueba scala.collection.JavaConversions, por lo que desde 2.12.0 una forma de hacerlo esto sería algo así como:

import scala.collection.JavaConverters._ 

// ... 

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

(observe la importación, la nueva es JavaConverters, en desuso se JavaConversions)

Cuestiones relacionadas