Ітерація над колекціями Java у Scala


113

Я пишу код Scala, який використовує API POI Apache . Я хотів би перебрати рядки, що містяться в тому, java.util.Iteratorщо я отримую з класу Sheet. Мені хотілося б використовувати ітератор у for eachциклі стилів, тому я намагався перетворити його на рідну колекцію Scala, але не пощастить.

Я переглянув класи / риси обгортки Scala, але не можу зрозуміти, як правильно ними користуватися. Як я повторюю колекцію Java у Scala, не використовуючи багатослівний while(hasNext()) getNext()стиль циклу?

Ось код, який я написав на основі правильної відповіді:

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

Я, здається, не можу включати рядок "for (val рядок <- рядки) {" без аналізатора, думаючи, що символ "<" є тегом XML? Повернення не працює
BefittingTheorem

Ви повинні мати можливість конвертувати в IteratirWrapper неявно, заощаджуючи неабиякий синтаксис. Google для неявних конверсій у Scala.
Даніель Шпієак

Відповіді:


28

Існує клас обгортки ( scala.collection.jcl.MutableIterator.Wrapper). Тож якщо ви визначаєте

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

тоді він буде діяти як підклас ітератора Scala, щоб ви могли це зробити foreach.


Він повинен читати: scala.collection.jcl.MutableIterator.Wrapper
samg

37
Ця відповідь застаріла в Scala 2.8; дивіться stackoverflow.com/questions/2708990/…
Alex R

256

Як і у Scala 2.8, все, що вам потрібно зробити, це імпортувати об’єкт JavaConversions, який вже оголошує відповідні перетворення.

import scala.collection.JavaConversions._

Однак це не працюватиме в попередніх версіях.


24

Редагувати : Scala 2.13.0 застаріле scala.collection.JavaConverters, тому з 2.13.0 вам потрібно використовувати scala.jdk.CollectionConverters.

Скала 2.12.0 знижується scala.collection.JavaConversions, тому, оскільки 2.12.0 один із способів цього буде чимось таким:

import scala.collection.JavaConverters._

// ...

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

(зауважте, імпорт, новий - JavaConverters, застарілий - JavaConversions)


2
Я шукав "toScala" ^ _ ^!
Profiterole

15

Правильна відповідь тут полягає у визначенні неявного перетворення з Java в Iteratorякийсь спеціальний тип. Цей тип повинен реалізувати foreachметод, який делегує основне Iterator. Це дозволить вам використовувати Scala for-loop з будь-якою Java Iterator.


9

Для Scala 2.10:

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

5

За допомогою Scala 2.10.4+ (а можливо і раніше) можна неявно перетворити java.util.Iterator [A] в scala.collection.Iterator [A] імпортом scala.collection.JavaConversions.asScalaIterator. Ось приклад:

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

Ви можете перетворити колекцію Java в масив і використовувати це:

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

Або перейдіть і перетворіть масив у список Scala:

val list = List.fromArray(array)

4

Якщо ви перебираєте через великий набір даних, ви, ймовірно, не хочете завантажувати цілу колекцію в пам'ять із .asScalaнеявним перетворенням. У цьому випадку зручним підходом є реалізація scala.collection.Iteratorознак

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

Він має подібну концепцію, але менш багатослівний IMO :)


2

Якщо ви хочете уникнути наслідків у scala.collection.JavaConversions, ви можете скористатися scala.collection.JavaConverters для явного перетворення.

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

Зверніть увагу на використання asScalaметоду для перетворення Java Iteratorв Scala Iterator.

JavaConverters доступні з Scala 2.8.1.


Я використовував, import scala.collection.JavaConverters._ а потім javaList.iterator().asScala із вашого прикладу, і це спрацювало
kosiara - Bartosz Kosarzycki
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.