Ich schreibe einen Code Scala, der die API Apache POI verwendet. Ich möchte die Zeilen durchlaufen, die in dem Java.util.Iterator
Enthalten sind, das ich von der Sheet-Klasse erhalte. Ich würde den Iterator gerne in einer for each
- Stilschleife verwenden, also habe ich versucht, ihn in eine native Scala - Sammlung zu konvertieren, habe aber kein Glück.
Ich habe mir die Wrapper-Klassen/Merkmale Scala angesehen, kann aber nicht erkennen, wie sie richtig verwendet werden. Wie kann ich eine Java - Sammlung in Scala durchlaufen, ohne den ausführlichen while(hasNext()) getNext()
-Schleifenstil zu verwenden?
Hier ist der Code, den ich basierend auf der richtigen Antwort geschrieben habe:
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)
}
}
}
Es gibt eine Wrapper-Klasse (scala.collection.jcl.MutableIterator.Wrapper
). Also, wenn Sie definieren
implicit def javaIteratorToScalaIterator[A](it : Java.util.Iterator[A]) = new Wrapper(it)
dann fungiert es als Unterklasse des Scala= Iterators, sodass Sie foreach
ausführen können.
Ab Scala 2.8 müssen Sie nur noch das JavaConversions-Objekt importieren, das bereits die entsprechenden Konvertierungen deklariert.
import scala.collection.JavaConversions._
Dies wird jedoch in früheren Versionen nicht funktionieren.
Bearbeiten: Scala 2.13.0 veraltet scala.collection.JavaConverters
, also seit 2.13.0 musst du scala.jdk.CollectionConverters
.
Scala 2.12.0 veraltet scala.collection.JavaConversions
, also seit 2.12.0 wäre eine Möglichkeit dies zu tun so etwas wie:
import scala.collection.JavaConverters._
// ...
for(k <- javaCollection.asScala) {
// ...
}
(Beachten Sie den Import, neu ist JavaConverters, veraltet ist JavaConversions)
Die richtige Antwort hier ist, eine implizite Konvertierung von Javas Iterator
in einen benutzerdefinierten Typ zu definieren. Dieser Typ sollte eine foreach
-Methode implementieren, die an das zugrunde liegende Iterator
delegiert. Auf diese Weise können Sie eine Scala for
- Schleife mit jedem Java Iterator
verwenden.
Für Scala 2.10:
// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
Mit Scala 2.10.4+ (und möglicherweise früher) ist es möglich, Java.util.Iterator [A] implizit in scala.collection.Iterator [A] zu konvertieren, indem scala.collection.JavaConversions importiert wird Hier ein Beispiel:
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
}
}
Sie können die Auflistung Java in ein Array konvertieren und Folgendes verwenden:
val array = Java.util.Arrays.asList("one","two","three").toArray
array.foreach(println)
Oder machen Sie weiter und konvertieren Sie das Array in eine Scala Liste:
val list = List.fromArray(array)
Wenn Sie einen großen Datensatz durchlaufen, möchten Sie wahrscheinlich nicht die gesamte Sammlung mit impliziter .asScala
- Konvertierung in den Speicher laden. In diesem Fall besteht ein praktischer Ansatz darin, das Merkmal scala.collection.Iterator
Zu implementieren
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
Es hat ein ähnliches Konzept, aber weniger ausführlich IMO :)
Wenn Sie die Auswirkungen von scala.collection.JavaConversions vermeiden möchten, können Sie scala.collection.JavaConverters verwenden, um explizit zu konvertieren .
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] = [email protected]
scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._
scala> i.asScala.mkString
res10: String = 12345678910
Beachten Sie die Verwendung der asScala
-Methode, um Java Iterator
in Scala Iterator
zu konvertieren.
Die JavaConverter sind seit Scala 2.8.1.