J'écris du code Scala qui utilise l'API Apache POI . J'aimerais parcourir les lignes contenues dans le Java.util.Iterator
Que je reçois. Je voudrais utiliser l’itérateur dans une boucle de style for each
, j’essaie donc de le convertir en une collection native Scala mais n’aurai aucune chance.
J'ai examiné les classes/traits de wrapper Scala, mais je ne vois pas comment les utiliser correctement. Comment puis-je parcourir une collection Java dans = Scala sans utiliser le verbose while(hasNext()) getNext()
style de la boucle?
Voici le code que j'ai écrit basé sur la réponse correcte:
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)
}
}
}
Il existe une classe wrapper (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 cependant.
Edit: Scala 2.13.0 déconseille scala.collection.JavaConverters
, donc depuis 2.13.0, vous devez utiliser scala.jdk.CollectionConverters
.
Scala 2.12.0 déconseille scala.collection.JavaConversions
, donc depuis 2.12.0, une façon de faire serait quelque chose comme:
import scala.collection.JavaConverters._
// ...
for(k <- javaCollection.asScala) {
// ...
}
(notez l'importation, JavaConverters est nouveau, JavaConversions est obsolète)
La bonne réponse ici est de définir une conversion implicite du Iterator
de Java en un type personnalisé. Ce type doit implémenter 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
Avec Scala 2.10.4+ (et peut-être plus tôt)), il est possible de convertir implicitement Java.util.Iterator [A] en scala.collection.Iterator [A] en important 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 l'utiliser:
val array = Java.util.Arrays.asList("one","two","three").toArray
array.foreach(println)
Ou continuez et convertissez le tableau en une liste Scala:
val list = List.fromArray(array)
Si vous parcourez un grand ensemble de données, vous ne voudrez probablement pas charger toute la collection en mémoire avec .asScala
conversion implicite. Dans ce cas, une approche pratique consiste à implémenter 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 un concept similaire mais moins bavard IMO :)
Si vous souhaitez éviter les implications de scala.collection.JavaConversions , vous pouvez utiliser scala.collection.JavaConverters 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
Notez l'utilisation de la méthode asScala
pour convertir le Java Iterator
en un Scala Iterator
.
Les JavaConverters sont disponibles depuis Scala 2.8.1.