Iterating over Java collections in Scala

♀尐吖头ヾ 提交于 2019-11-26 21:25:46

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.

ttonelli

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.

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)

Daniel Spiewak

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.

F. P. Freely

For Scala 2.10:

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

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
  }

}

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)

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!