114
votes

I'm writing some Scala code which uses the Apache POI API. I would like to iterate over the rows contained in the java.util.Iterator that I get from the Sheet class. I would like to use the iterator in a for each style loop, so I have been trying to convert it to a native Scala collection but will no luck.

I have looked at the Scala wrapper classes/traits, but I can not see how to use them correctly. How do I iterate over a Java collection in Scala without using the verbose while(hasNext()) getNext() style of loop?

Here's the code I wrote based on the correct answer:

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
I can't seem to include the line "for (val row <- rows){" without the parser thinking the '<' character is an XML closing tag? The backticks do not workBefittingTheorem
You should be able to convert to IteratirWrapper implicitly, saving you a fair bit of syntax. Google for implicit conversions in Scala.Daniel Spiewak

9 Answers

29
votes

There is a wrapper class (scala.collection.jcl.MutableIterator.Wrapper). So if you define

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

then it will act as a sub class of the Scala iterator so you can do foreach.

258
votes

As of Scala 2.8, all you have to do is to import the JavaConversions object, which already declares the appropriate conversions.

import scala.collection.JavaConversions._

This won't work in previous versions though.

31
votes

Edit: Scala 2.13.0 deprecates scala.collection.JavaConverters, so since 2.13.0 you need to use scala.jdk.CollectionConverters.

Scala 2.12.0 deprecates scala.collection.JavaConversions, so since 2.12.0 one way of doing this would be something like:

import scala.collection.JavaConverters._

// ...

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

(notice the import, new is JavaConverters, deprecated is JavaConversions)

15
votes

The correct answer here is to define an implicit conversion from Java's Iterator to some custom type. This type should implement a foreach method which delegates to the underlying Iterator. This will allow you to use a Scala for-loop with any Java Iterator.

9
votes

For Scala 2.10:

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

With Scala 2.10.4+ (and possibly earlier) it is possible to implicitly convert java.util.Iterator[A] to scala.collection.Iterator[A] by importing scala.collection.JavaConversions.asScalaIterator. Here is an example:

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
  }

}
5
votes

If you are iterating through a large dataset, then you probably don't want to load whole collection into memory with .asScala implicit conversion. In this case, a handy way approach is to implement 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

It has similar concept but less verbose IMO :)

4
votes

You could convert the Java collection to an array and use that:

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

Or go on and convert the array to a Scala list:

val list = List.fromArray(array)
2
votes

If you would like to avoid the implicits in scala.collection.JavaConversions you can use scala.collection.JavaConverters to convert explicitly.

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

Note the use of the asScala method to convert the Java Iterator to a Scala Iterator.

The JavaConverters have been available since Scala 2.8.1.