116

我正在编写一些使用Apache POI API 的 Scala 代码。我想遍历java.util.Iterator从 Sheet 类中获得的行中包含的行。我想在for each样式循环中使用迭代器,所以我一直在尝试将它转换为原生 Scala 集合,但不会成功。

我查看了 Scala 包装器类/特征,但看不到如何正确使用它们。如何在不使用冗长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)
        }
    }
}
4

9 回答 9

258

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

import scala.collection.JavaConversions._

不过,这在以前的版本中不起作用。

于 2009-10-26T14:45:34.197 回答
32

编辑:Scala 2.13.0 已弃用scala.collection.JavaConverters,因此从 2.13.0 开始,您需要使用scala.jdk.CollectionConverters.

Scala 2.12.0 deprecates scala.collection.JavaConversions,所以从 2.12.0 开始,这样做的一种方法是:

import scala.collection.JavaConverters._

// ...

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

(注意导入,new 是 JavaConverters,deprecated 是 JavaConversions)

于 2017-05-31T17:25:21.627 回答
29

有一个包装类 ( scala.collection.jcl.MutableIterator.Wrapper)。所以如果你定义

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

那么它将充当 Scala 迭代器的子类,因此您可以执行foreach.

于 2009-08-03T16:24:41.310 回答
15

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

于 2009-01-30T15:48:24.723 回答
9

对于 Scala 2.10:

// Feature warning if you don't enable implicit conversions...
import scala.language.implicitConversions
import scala.collection.convert.WrapAsScala.enumerationAsScalaIterator
于 2013-02-21T18:01:26.733 回答
5

使用 Scala 2.10.4+(可能更早),可以通过导入 scala.collection.JavaConversions.asScalaIterator 将 java.util.Iterator[A] 隐式转换为 scala.collection.Iterator[A]。这是一个例子:

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
  }

}
于 2015-05-16T20:09:09.643 回答
5

如果您正在遍历大型数据集,那么您可能不想通过.asScala隐式转换将整个集合加载到内存中。在这种情况下,一种方便的方法是实现scala.collection.Iteratortrait

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

于 2016-01-01T00:43:51.373 回答
4

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

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

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

val list = List.fromArray(array)
于 2009-01-30T15:58:31.317 回答
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 开始可用。

于 2015-08-07T06:59:28.983 回答