การทำซ้ำคอลเลกชัน Java ใน Scala


113

ฉันกำลังเขียนโค้ด Scala ซึ่งใช้Apache POI API ฉันต้องการทำซ้ำในแถวที่อยู่ในjava.util.Iteratorที่ได้รับจากคลาสชีต ฉันต้องการใช้ตัววนซ้ำในfor eachรูปแบบลูปดังนั้นฉันจึงพยายามแปลงเป็นคอลเลกชัน Scala ดั้งเดิม แต่จะไม่มีโชค

ฉันได้ดูคลาส / ลักษณะของ Scala wrapper แล้ว แต่ไม่เห็นวิธีใช้อย่างถูกต้อง ฉันจะทำซ้ำคอลเลกชัน 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)
        }
    }
}

ดูเหมือนว่าฉันจะไม่สามารถรวมบรรทัด "for (val row <- backticks ไม่ทำงาน
BefittingTheorem

คุณควรจะสามารถแปลงเป็น IteratirWrapper ได้โดยปริยายซึ่งจะช่วยประหยัดไวยากรณ์ได้พอสมควร Google สำหรับการแปลงโดยนัยใน Scala
Daniel Spiewak

คำตอบ:


28

มีคลาส wrapper ( scala.collection.jcl.MutableIterator.Wrapper) ดังนั้นถ้าคุณกำหนด

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

แล้วก็จะทำหน้าที่เป็นชั้นย่อยของ iterator Scala foreachเพื่อให้คุณสามารถทำ


ควรอ่าน: scala.collection.jcl.MutableIterator.Wrapper
samg

37
คำตอบนี้ล้าสมัยใน Scala 2.8; ดูstackoverflow.com/questions/2708990/…
Alex R

256

สำหรับ Scala 2.8 สิ่งที่คุณต้องทำคือการนำเข้าออบเจ็กต์ JavaConversions ซึ่งประกาศการแปลงที่เหมาะสมแล้ว

import scala.collection.JavaConversions._

สิ่งนี้จะใช้ไม่ได้กับเวอร์ชันก่อนหน้า


24

แก้ไข : Scala 2.13.0 เลิกใช้งานscala.collection.JavaConvertersดังนั้นตั้งแต่ 2.13.0 คุณต้องใช้scala.jdk.CollectionConvertersคุณจำเป็นต้องใช้

Scala 2.12.0 เลิกใช้งานscala.collection.JavaConversionsดังนั้นตั้งแต่ 2.12.0 วิธีหนึ่งในการทำสิ่งนี้จะเป็นดังนี้:

import scala.collection.JavaConverters._

// ...

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

(สังเกตการนำเข้าใหม่คือ JavaConverters เลิกใช้แล้วคือ JavaConversions)


2
ฉันกำลังมองหา "toScala" ^ _ ^!
Profiterole

15

คำตอบที่ถูกต้องในที่นี้คือการกำหนดการแปลงโดยนัยจาก Java เป็นIteratorประเภทกำหนดเองบางประเภท ประเภทนี้ควรใช้วิธีการซึ่งได้รับมอบหมายให้อยู่ภายใต้foreach Iteratorนี้จะช่วยให้คุณใช้ Scala for-loop กับ IteratorJava



5

ด้วย 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
  }

}

4

คุณสามารถแปลงคอลเลกชัน 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 น้อยกว่า :)


2

หากคุณต้องการหลีกเลี่ยงความหมายใน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เพื่อ IteratorScala

JavaConverters มีให้ใช้งานตั้งแต่ Scala 2.8.1


ผมใช้import scala.collection.JavaConverters._ แล้ว javaList.iterator().asScala จากตัวอย่างของคุณและมันทำงาน
kosiara - Bartosz Kosarzycki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.