Itération des collections Java en Scala

j'écris un code Scala qui utilise L'API Apache POI . Je voudrais itérer au-dessus des lignes contenues dans le java.util.Iterator que je reçois de la classe de feuille. Je voudrais utiliser l'itérateur dans une boucle de style for each , donc j'ai essayé de le convertir à une collection Scala natif, mais ne sera pas de chance.

j'ai regardé les classes/traits d'emballage Scala, mais je ne vois pas comment les utiliser correctement. Comment puis-je effectuer une itération sur un Collection Java en Scala sans utiliser le style de boucle while(hasNext()) getNext() verbeux?

voici le code que j'ai écrit basé sur la bonne réponse:

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)
        }
    }
}
102
demandé sur Brian Heylin 2009-01-30 17:47:48

9 réponses

il existe une classe d'emballage ( scala.collection.jcl.MutableIterator.Wrapper ). Donc si vous définissez

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

alors il agira comme une sous-classe de L'itérateur Scala afin que vous puissiez faire foreach .

25
répondu 4 revs, 4 users 67%James 2014-07-06 01:15:51

à partir de Scala 2.8, tout ce que vous avez à faire est d'importer L'objet JavaConversions, qui déclare déjà les conversions appropriées.

import scala.collection.JavaConversions._

cela ne fonctionnera pas dans les versions précédentes.

242
répondu ttonelli 2013-06-09 20:18:35

la bonne réponse ici est de définir une conversion implicite du Iterator de Java vers un certain type personnalisé. Ce type devrait mettre en œuvre une méthode foreach qui délègue au Iterator sous-jacent . Cela vous permettra d'utiliser une boucle Scala for -avec N'importe quel Java Iterator .

15
répondu Daniel Spiewak 2013-06-09 20:16:10

Pour Scala 2.10:

// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
9
répondu F. P. Freely 2013-06-09 20:18:52

Scala 2.12.0 déprécie scala.collection.JavaConversions , donc depuis 2.12.0 une façon de faire ceci serait quelque chose comme:

import scala.collection.JavaConverters._

// ...

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

(avis de l'importation, de nouveau, c'est JavaConverters, obsolète est JavaConversions)

9
répondu antonone 2017-05-31 17:25:21

avec Scala 2.10.4+ (et peut-être plus tôt) il est possible de convertir implicitement java.util.Itérateur[A] à la scala.collection.Itérateur[A] par l'importation de scala.collection.JavaConversions.asScalaIterator. Voici un exemple:

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
  }

}
5
répondu user4322779 2015-05-16 20:09:09

vous pouvez convertir la collection Java en un tableau et utiliser que:

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

ou allez sur et convertissez le tableau à une liste de Scala:

val list = List.fromArray(array)
4
répondu Fabian Steeg 2009-01-30 15:58:31

si vous itérez à travers un grand ensemble de données, alors vous ne voulez probablement pas charger toute la collection dans la mémoire avec la conversion implicite .asScala . Dans ce cas, une approche pratique est de mettre en œuvre scala.collection.Iterator trait

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

il a le même concept mais moins verbeux IMO :)

3
répondu Max 2016-01-01 00:43:51

si vous voulez éviter les implications dans scala.collection.JavaConversions vous pouvez utiliser scala.collection.Javaconverter pour convertir explicitement.

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

Remarque l'utilisation de la asScala méthode pour convertir la Java Iterator à un Scala Iterator .

les Javaconverter sont disponibles depuis Scala 2.8.1.

2
répondu kilo 2015-08-07 06:59:28