Iterazione sulle raccolte Java in Scala


113

Sto scrivendo del codice Scala che utilizza l' API POI di Apache . Vorrei iterare sulle righe contenute nel java.util.Iteratorche ottengo dalla classe Sheet. Vorrei utilizzare l'iteratore in un for eachciclo di stile, quindi ho provato a convertirlo in una raccolta Scala nativa, ma non avrò fortuna.

Ho esaminato le classi / tratti del wrapper Scala, ma non riesco a vedere come usarli correttamente. Come faccio a iterare su una raccolta Java in Scala senza utilizzare lo while(hasNext()) getNext()stile dettagliato di loop?

Ecco il codice che ho scritto in base alla risposta corretta:

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

Non riesco a includere la riga "for (val row <- rows) {" senza che il parser pensi che il carattere '<' sia un tag di chiusura XML? I backtick non funzionano
BefittingTheorem

Dovresti essere in grado di convertire in IteratirWrapper implicitamente, risparmiando un bel po 'di sintassi. Google per conversioni implicite in Scala.
Daniel Spiewak

Risposte:


28

C'è una classe wrapper ( scala.collection.jcl.MutableIterator.Wrapper). Quindi se definisci

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

quindi agirà come una sottoclasse dell'iteratore Scala, quindi puoi farlo foreach.


Dovrebbe essere visualizzato: scala.collection.jcl.MutableIterator.Wrapper
samg

37
Questa risposta è obsoleta in Scala 2.8; vedi stackoverflow.com/questions/2708990/…
Alex R

256

A partire da Scala 2.8, tutto ciò che devi fare è importare l'oggetto JavaConversions, che dichiara già le conversioni appropriate.

import scala.collection.JavaConversions._

Questo però non funzionerà nelle versioni precedenti.


24

Modifica : Scala 2.13.0 è deprecato scala.collection.JavaConverters, quindi dalla 2.13.0 è necessario utilizzare scala.jdk.CollectionConverters.

Scala 2.12.0 è deprecato scala.collection.JavaConversions, quindi dalla 2.12.0 un modo per farlo sarebbe qualcosa del tipo:

import scala.collection.JavaConverters._

// ...

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

(nota l'importazione, il nuovo è JavaConverters, il deprecato è JavaConversions)


2
Stavo cercando "toScala" ^ _ ^!
Profiterole

15

La risposta corretta qui è definire una conversione implicita da Java Iteratora un tipo personalizzato. Questo tipo dovrebbe implementare un foreachmetodo che delega al sottostante Iterator. Questo ti permetterà di usare un forciclo di Scala con qualsiasi Java Iterator.


9

Per 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+ (e forse anche prima) è possibile convertire implicitamente java.util.Iterator [A] in scala.collection.Iterator [A] importando scala.collection.JavaConversions.asScalaIterator. Ecco un esempio:

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

È possibile convertire la raccolta Java in un array e utilizzarlo:

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

Oppure vai avanti e converti l'array in un elenco Scala:

val list = List.fromArray(array)

4

Se si esegue l'iterazione di un set di dati di grandi dimensioni, probabilmente non si desidera caricare l'intera raccolta in memoria con la .asScalaconversione implicita. In questo caso, un approccio pratico è implementare il scala.collection.Iteratortratto

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 un concetto simile ma meno prolisso IMO :)


2

Se vuoi evitare gli impliciti in scala.collection.JavaConversions puoi usare scala.collection.JavaConverters per convertire esplicitamente.

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

Notare l'uso del asScalametodo per convertire Java Iteratorin Scala Iterator.

I JavaConverters sono disponibili da Scala 2.8.1.


Ho usato import scala.collection.JavaConverters._ e poi javaList.iterator().asScala dal tuo esempio e ha funzionato
kosiara - Bartosz Kosarzycki
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.