Итерация по коллекциям Java в Scala

113

Я пишу код Scala, который использует API Apache POI . Я хотел бы перебрать строки, содержащиеся в том, 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 row <- rows) {» без того, чтобы синтаксический анализатор решил, что символ «<» является закрывающим тегом XML? Обратные кавычки не работают
BefittingTheorem
Вы должны иметь возможность неявно конвертировать в IteratirWrapper, что сэкономит вам немного синтаксиса. Google для неявных преобразований в Scala.
Даниэль Спивак,

Ответы:

28

Есть обертка class ( 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._

Однако это не будет работать в предыдущих версиях.

ttonelli
источник
24

Изменить : Scala 2.13.0 устарел scala.collection.JavaConverters, поэтому с 2.13.0 вам нужно использовать scala.jdk.CollectionConverters.

Scala 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. Это позволит вам использовать forцикл Scala с любой 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
FP Бесплатно
источник
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
  }

}
user4322779
источник
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

Он имеет аналогичную концепцию, но менее подробный ИМО :)

Максимум
источник
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 из твоего примера, и это сработало
Косиара - Бартош Косажицкий