wake-up-neo.com

Durchlaufen von Java Sammlungen in Scala

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

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.

26
James

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.

251
ttonelli

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)

16
antonone

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.

15
Daniel Spiewak

Für 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

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
  }

}
5
user4322779

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

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

3
Max

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.

2
kilo