web-dev-qa-db-fra.com

Itération sur Java collections dans Scala

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

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.

26
James

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

251
ttonelli

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)

16
antonone

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.

15
Daniel Spiewak

Pour Scala 2.10:

// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
9
F. P. Freely

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
  }

}
5
user4322779

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)
4
Fabian Steeg

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

3
Max

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.

2
kilo