在Scala中迭代Java集合

我在写一些使用Apache POI API的Scala代码。 我想迭代包含在我从Sheet类中得到的java.util.Iterator的行。 我想在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) } } } 

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

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

那么它将作为Scala迭代器的子类,所以你可以做foreach

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

 import scala.collection.JavaConversions._ 

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

这里的正确答案是定义从Java的Iterator到一些自定义types的隐式转换。 这个types应该实现一个foreach方法,委托给底层的Iterator 。 这将允许您使用任何Java Iterator的Scala for -loop。

对于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+(可能更早),可以通过导入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 } } 

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

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

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

 val list = List.fromArray(array) 

Scala 2.12.0弃用了scala.collection.JavaConversions ,所以从2.12.0开始,一种方法就是这样:

 import scala.collection.JavaConverters._ // ... for(k <- javaCollection.asScala) { // ... } 

(注意导入,新是JavaConverters,不推荐使用JavaConversions)

如果你想避免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

自从Scala 2.8.1以来,JavaConverters已经可用了。

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

它有类似的概念,但较less冗长IMO 🙂