在 Scala 中迭代 Java 集合

我正在编写一些使用 阿帕奇警察API 的 Scala 代码。我想循环遍历包含在 java.util.Iterator中的行,这些行是我从 Sheet 类中获得的。我想在 for each样式的循环中使用迭代器,所以我一直试图将它转换成一个本地 Scala 集合,但是没有成功。

我已经研究过 Scala 包装类/trait,但是我不知道如何正确地使用它们。如何在不使用详细的 while(hasNext()) getNext()循环样式的情况下在 Scala 中迭代 Java 集合?

下面是我根据正确答案编写的代码:

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)
}
}
}
86932 次浏览

这里的正确答案是定义从 Java 的 Iterator到某种定制类型的隐式转换。此类型应实现一个 foreach方法,该方法委托给底层 Iterator。这将允许您对任何 JavaIterator使用 Scalafor循环。

您可以将 Java 集合转换为数组并使用:

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

或者继续将数组转换为 Scala 列表:

val list = List.fromArray(array)

有一个包装类(scala.collection.jcl.MutableIterator.Wrapper)

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

然后它将作为 Scala 迭代器的一个子类,这样您就可以执行 foreach了。

从 Scala 2.8开始,您所要做的就是导入 JavaConversion 对象,该对象已经声明了适当的转换。

import scala.collection.JavaConversions._

但是这在以前的版本中不起作用。

对于 Scala 2.10:

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

使用 Scala 2.10.4 + (可能更早) ,可以隐式转换 java.util。到 scala.Collection 的迭代器[ A ]。通过导入 scala.Collection 迭代器[ A ]。JavaConversons.asScalalterator.这里有一个例子:

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
}


}

如果希望避免 集合,JavaConversion中的隐含内容,可以使用 集合,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方法将 JavaIterator转换为 ScalaIterator

JavaConverters 从 Scala 2.8.1开始就可以使用了。

如果您正在迭代一个大型数据集,那么您可能不希望使用 .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

它有类似的概念,但不太冗长的 IMO:)

编辑 : 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,不推荐的是 JavaConversion)