Durchlaufen von Java-Sammlungen in Scala


113

Ich schreibe einen Scala-Code, der die Apache POI- API verwendet. Ich möchte über die Zeilen iterieren, die in java.util.Iteratorder Sheet-Klasse enthalten sind. Ich möchte den Iterator in einer for eachStilschleife verwenden, daher habe ich versucht, ihn in eine native Scala-Sammlung zu konvertieren, werde aber kein Glück haben.

Ich habe mir die Scala-Wrapper-Klassen / -Eigenschaften angesehen, kann aber nicht sehen, wie man sie richtig verwendet. Wie iteriere ich über eine Java-Sammlung in Scala, ohne den ausführlichen while(hasNext()) getNext()Stil der Schleife 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)
        }
    }
}

Ich kann die Zeile "for (val row <- rows) {" scheinbar nicht einfügen, ohne dass der Parser denkt, dass das Zeichen '<' ein XML-Abschluss-Tag ist. Die Backticks funktionieren nicht
— BefittingTheorem

Sie sollten in der Lage sein, implizit in IteratirWrapper zu konvertieren, wodurch Sie ein gutes Stück Syntax sparen. Google für implizite Conversions in Scala.
— Daniel Spiewak

Antworten:


28

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 dies tun können foreach.


Es sollte lauten: scala.collection.jcl.MutableIterator.Wrapper
— samg

37
Diese Antwort ist in Scala 2.8 veraltet. siehe stackoverflow.com/questions/2708990/…
— Alex R

256

Ab Scala 2.8 müssen Sie lediglich das JavaConversions-Objekt importieren, das bereits die entsprechenden Konvertierungen deklariert.

import scala.collection.JavaConversions._

Dies funktioniert jedoch in früheren Versionen nicht.


24

Bearbeiten : Scala 2.13.0 ist veraltet scala.collection.JavaConverters, daher müssen Sie seit 2.13.0 verwenden scala.jdk.CollectionConverters.

Scala 2.12.0 ist veraltet scala.collection.JavaConversions. Seit 2.12.0 wäre eine Möglichkeit, dies zu tun, etwa:

import scala.collection.JavaConverters._

// ...

for(k <- javaCollection.asScala) {
    // ...
}

(Beachten Sie den Import, neu ist JavaConverters, veraltet ist JavaConversions)


2
Ich suchte nach "toScala" ^ _ ^!
— Profiterole

15

Die richtige Antwort besteht darin, eine implizite Konvertierung von Java Iteratorin einen benutzerdefinierten Typ zu definieren. Dieser Typ sollte eine foreachMethode implementieren , die an den Basiswert delegiert Iterator. Auf diese Weise können Sie eine Scala- forSchleife mit jedem Java verwenden Iterator.


9

Für Scala 2.10:

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

5

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.asScalaIterator importiert wird. Hier ist 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
  }

}

4

Sie können die Java-Sammlung in ein Array konvertieren und Folgendes verwenden:

val array = java.util.Arrays.asList("one","two","three").toArray
array.foreach(println)

Oder konvertieren Sie das Array in eine Scala-Liste:

val list = List.fromArray(array)

4

Wenn Sie ein großes Dataset durchlaufen, möchten Sie wahrscheinlich nicht die gesamte Sammlung mit .asScalaimpliziter Konvertierung in den Speicher laden . In diesem Fall besteht ein praktischer Ansatz darin, scala.collection.IteratorMerkmale 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 :)


2

Wenn Sie die Implikationen in 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] = java.util.LinkedList$ListItr@11eadcba

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> i.asScala.mkString
res10: String = 12345678910

Beachten Sie die Verwendung der asScalaMethode zum Konvertieren von Java Iteratorin eine Scala Iterator.

Die JavaConverter sind seit Scala 2.8.1 verfügbar.


Ich habe import scala.collection.JavaConverters._ und dann javaList.iterator().asScala von Ihrem Beispiel und es hat funktioniert
— kosiara - Bartosz Kosarzycki
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.