首页 文章

在Scala中迭代Java集合

提问于
浏览
107

我正在编写一些使用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)
        }
    }
}

9 回答

  • 25

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

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

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

  • 9

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

    import scala.collection.JavaConversions._
    

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

  • 248

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

  • 5

    Scala 2.12.0弃用 scala.collection.JavaConversions ,所以从2.12.0开始,这样做的方法如下:

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

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

  • 2

    对于Scala 2.10:

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

    使用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
      }
    
    }
    
  • 14

    您可以将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
    

    它有类似的概念但不那么冗长的IMO :)

  • 15

    如果您想避免scala.collection.JavaConversions中的implicits,您可以使用 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一直可用 .

相关问题