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)
}
}
}
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
.
à 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.
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
.
Pour 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 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)
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
}
}
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)
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 :)
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.