Итерация над compilationами Java в Scala

Я пишу код Scala, который использует API Apache POI . Я хотел бы перебрать строки, содержащиеся в java.util.Iterator которые я получаю из classа Sheet. Я хотел бы использовать iterator в for each цикле стиля, поэтому я пытался преобразовать его в родную коллекцию Scala, но не повезет.

Я просмотрел classы / черты оболочки Scala, но я не вижу, как их правильно использовать. Как выполнить итерацию по коллекции Java в Scala без использования многословного while(hasNext()) getNext() цикла?

Вот код, который я написал на основе правильного ответа:

 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) } } } в 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) } } } 

Существует class-shell ( scala.collection.jcl.MutableIterator.Wrapper ). Поэтому, если вы определите

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

то он будет выступать в качестве подclassа iteratorа Scala, чтобы вы могли сделать foreach .

Начиная с Scala 2.8, все, что вам нужно сделать, это импортировать объект JavaConversions, который уже объявляет соответствующие преобразования.

 import scala.collection.JavaConversions._ 

Однако это не будет работать в предыдущих версиях.

Правильный ответ здесь заключается в том, чтобы определить неявное преобразование из Iterator Java в некоторый нестандартный тип. Этот тип должен реализовать метод foreach который делегирует базовому Iterator . Это позволит вам использовать Scala for -loop с любым Java- Iterator .

Для Scala 2.10:

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

Scala 2.12.0 обесценивает scala.collection.JavaConversions , поэтому с 2.12.0 одним из способов сделать это будет что-то вроде:

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

(обратите внимание на импорт, новый - JavaConverters, устаревший - JavaConversions)

С Scala 2.10.4+ (и, возможно, ранее) можно неявно преобразовать java.util.Iterator [A] в scala.collection.Iterator [A] путем импорта scala.collection.JavaConversions.asScalaIterator. Вот пример:

 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) 

Если вы выполняете итерацию через большой dataset, вы, вероятно, не хотите загружать целую коллекцию в память с .asScala неявного преобразования .asScala . В этом случае удобный подход – реализовать 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 

Он имеет схожую концепцию, но менее подробный ИМО 🙂

Если вы хотите избежать implicits в 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] = [email protected] scala> import scala.collection.JavaConverters._ import scala.collection.JavaConverters._ scala> i.asScala.mkString res10: String = 12345678910 

Обратите внимание на использование метода asScala для преобразования Iterator Java в Scala Iterator .

JavaConverters доступны со Scala 2.8.1.

  • Как включить список списков в список в Java 8?
  • Выберите N случайных элементов из списка в C #
  • Почему должен быть предпочтительным интерфейс для classа Java?
  • Есть ли способ проверить, содержат ли две коллекции одни и те же элементы, независимо от порядка?
  • Вывод Big-O для реализации Java Collections Framework?
  • Как создать новый список на Java
  • Как сделать 2 сопоставимых метода только в одном classе?
  • Как скопировать файл java.util.List в другой файл java.util.List
  • Привязка списка в @RequestParam
  • В каких ситуациях подходит CopyOnWriteArrayList?
  • Каков наилучший способ реализации streamобезопасного словаря?
  • Давайте будем гением компьютера.