Итерирование над наборами Java в Scala
Я пишу код 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)
}
}
}
Ответы
Ответ 1
Существует класс-оболочка (scala.collection.jcl.MutableIterator.Wrapper
). Поэтому, если вы определяете
implicit def javaIteratorToScalaIterator[A](it : java.util.Iterator[A]) = new Wrapper(it)
то он будет действовать как подкласс для итератора Scala, поэтому вы можете сделать foreach
.
Ответ 2
В соответствии с Scala 2.8 все, что вам нужно сделать, это импортировать объект JavaConversions, который уже объявляет соответствующие преобразования.
import scala.collection.JavaConversions._
Однако это не работает в предыдущих версиях.
Ответ 3
Изменить: 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, устаревшим является JavaConververs)
Ответ 4
Правильный ответ здесь заключается в том, чтобы определить неявное преобразование из Java Iterator
в некоторый нестандартный тип. Этот тип должен реализовать метод foreach
, который делегирует базовый Iterator
. Это позволит вам использовать Scala for
-loop с любым Java Iterator
.
Ответ 5
Для Scala 2.10:
// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
Ответ 6
С 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
}
}
Ответ 7
Вы можете преобразовать коллекцию Java в массив и использовать ее:
val array = java.util.Arrays.asList("one","two","three").toArray
array.foreach(println)
Или включите и преобразуйте массив в список Scala:
val list = List.fromArray(array)
Ответ 8
Если вы выполняете итерацию через большой набор данных, вы, вероятно, не хотите загружать целую коллекцию в память с помощью .asScala
неявного преобразования. В этом случае удобный подход - реализовать scala.collection.Iterator
trait
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
У него есть аналогичная концепция, но менее многословная ИМО:)
Ответ 9
Если вы хотите избежать implicits в 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] = [email protected]
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.