Iterasi koleksi Java di Scala

113

Saya sedang menulis beberapa kode Scala yang menggunakan Apache POI API. Saya ingin mengulang baris yang ada di java.util.Iteratoryang saya dapatkan dari kelas Sheet. Saya ingin menggunakan iterator dalam for eachloop gaya, jadi saya telah mencoba mengubahnya menjadi koleksi Scala asli tetapi tidak berhasil.

Saya telah melihat kelas / sifat pembungkus Scala, tetapi saya tidak dapat melihat cara menggunakannya dengan benar. Bagaimana cara saya mengulang koleksi Java di Scala tanpa menggunakan while(hasNext()) getNext()gaya loop verbose ?

Berikut kode yang saya tulis berdasarkan jawaban yang benar:

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)
        }
    }
}
Brian Heylin
sumber
Saya tidak dapat menyertakan baris "for (val row <- row) {" tanpa pengurai berpikir bahwa karakter '<' adalah tag penutup XML? Backticks tidak berfungsi
BefittingTheorem
Anda harus dapat mengonversi ke IteratirWrapper secara implisit, menghemat sedikit sintaks. Google untuk konversi implisit di Scala.
Daniel Spiewak

Jawaban:

28

Ada kelas pembungkus ( scala.collection.jcl.MutableIterator.Wrapper). Jadi jika Anda mendefinisikan

implicit def javaIteratorToScalaIterator[A](it : java.util.Iterator[A]) = new Wrapper(it)

maka itu akan bertindak sebagai sub kelas dari iterator Scala sehingga Anda bisa melakukannya foreach.

James
sumber
Seharusnya terbaca: scala.collection.jcl.MutableIterator.Wrapper
samg
37
Jawaban ini sudah usang di Scala 2.8; lihat stackoverflow.com/questions/2708990/…
Alex R
256

Mulai dari Scala 2.8, yang harus Anda lakukan adalah mengimpor objek JavaConversions, yang sudah mendeklarasikan konversi yang sesuai.

import scala.collection.JavaConversions._

Ini tidak akan berfungsi di versi sebelumnya.

ttonelli
sumber
24

Edit : Scala 2.13.0 tidak berlaku lagi scala.collection.JavaConverters, jadi sejak 2.13.0 Anda perlu menggunakan scala.jdk.CollectionConverters.

Scala 2.12.0 tidak berlaku lagi scala.collection.JavaConversions, jadi sejak 2.12.0 salah satu cara untuk melakukannya adalah seperti ini:

import scala.collection.JavaConverters._

// ...

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

(perhatikan impornya, baru JavaConverters, usang adalah JavaConversions)

antonon
sumber
2
Saya mencari "toScala" ^ _ ^!
Profiterole
15

Jawaban yang benar di sini adalah untuk menentukan konversi implisit dari Java Iteratorke beberapa tipe kustom. Jenis ini harus menerapkan foreachmetode yang mendelegasikan ke yang mendasarinya Iterator. Ini akan memungkinkan Anda menggunakan Scala for-loop dengan Java apa pun Iterator.

Daniel Spiewak
sumber
9

Untuk Scala 2.10:

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

Dengan Scala 2.10.4+ (dan mungkin sebelumnya), Anda dapat secara implisit mengonversi java.util.Iterator [A] ke scala.collection.Iterator [A] dengan mengimpor scala.collection.JavaConversions.asScalaIterator. Berikut ini contohnya:

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
  }

}
pengguna4322779
sumber
4

Anda bisa mengonversi koleksi Java ke array dan menggunakannya:

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

Atau lanjutkan dan ubah larik menjadi daftar Scala:

val list = List.fromArray(array)
Fabian Steeg
sumber
4

Jika Anda melakukan iterasi melalui kumpulan data yang besar, Anda mungkin tidak ingin memuat seluruh koleksi ke dalam memori dengan .asScalakonversi implisit. Dalam hal ini, pendekatan cara praktis adalah dengan mengimplementasikan scala.collection.Iteratorsifat

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

Ini memiliki konsep yang mirip tetapi IMO kurang verbose :)

Max
sumber
2

Jika Anda ingin menghindari implikasi dalam scala.collection.JavaConversions, Anda dapat menggunakan scala.collection.JavaConverters untuk mengonversi secara eksplisit.

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

Perhatikan penggunaan asScalametode untuk mengonversi Java Iteratorke Scala Iterator.

JavaConverters telah tersedia sejak Scala 2.8.1.

kilo
sumber
Saya menggunakan import scala.collection.JavaConverters._ dan kemudian javaList.iterator().asScala dari contoh Anda dan berhasil
kosiara - Bartosz Kosarzycki