ScalaのListとJavaのListと配列(使い方や相互変換)
ScalaのListとJavaのListの使い方とかについてのメモです。
scala.List
scala.Listはイミュータブルです。Javaのようにaddしたりはできません。
addしたいのであれば以下のように再生成する必要があります。
package com.tsukaby.scala.sample object Main { def main(args: Array[String]) { var scalaList: List[Int] = List(1, 2, 3) scalaList = scalaList ::: List(4) for (value <- scalaList) { println(f"value=$value%d") } } }
:::はリストを連結して新しいリストを返却します。
scala.collection.mutable.ListBuffer
Javaユーザにとって上記のリストはなかなか辛い物があります。
ListBufferはパッケージ名から分かる通りミュータブルなので内部の構造を変えることができます。つまりJavaで言うArrayListと同じようなことができます。
package com.tsukaby.scala.sample import scala.collection.mutable.ListBuffer object Main { def main(args: Array[String]) { val buf: ListBuffer[Int] = ListBuffer(1, 2, 3) buf += 4 for (value <- buf.toList) { println(f"value=$value%d") } } }
+=で要素を追加しており、toListでリストに変換しています。
java.util.Listとjava.util.ArrayList
Scalaでは普通にJavaのライブラリなどが使えます。Java用のライブラリを利用する場合、そのライブラリがjava.util.Listを返却してくる場合があるかと思います。そういうケースも考慮してScalaでJavaのListも使えるようになっておいた方が良いと思います。
package com.tsukaby.scala.sample import scala.collection.JavaConversions._ object Main { def main(args: Array[String]) { val javaList: java.util.List[Int] = new java.util.ArrayList[Int]() javaList.add(1) javaList.add(2) javaList.add(3) javaList.add(4) for (value <- javaList) { println(f"value=$value%d") } } }
大体いつものJavaと同じ感じです。ArrayListはnewする必要があります。
また、JavaConversionsという暗黙の型変換を行ってくれるクラスをimportしてください。これが無いとfor式の部分で以下のようにコンパイルエラーが出てしまいます。
Error:(14, 19) value foreach is not a member of java.util.List[Int] for (value <- javaList) { ^
上記のfor式はあくまでScala用なのでJavaのListを受け付けてくれません。JavaConversionsをimportすると暗黙的にScalaのListに変換してくれるのでコンパイルが通る、という訳です。
java.util.Listからscala.Listへの変換
使用するライブラリなどによってscala.Listを受け付けているのか、java.util.Listを受け付けているのか変わってくると思います。上記2つは同じListとは言え、別の型ですので、ちゃんと意識しないとコンパイルエラーになります。
例えば先ほどの例とほぼ同じですが、以下はコンパイルエラーになります。
package com.tsukaby.scala.sample object Main { def main(args: Array[String]) { val javaList: java.util.List[Int] = new java.util.ArrayList[Int]() printListContents(javaList) } def printListContents(list: scala.List[Int]) { for (value <- list) { println(f"value=$value%d") } } }
(Intellij上だとType mismatch, expected: List[Int], actual: List[Int]と出て一瞬「!?」となります。)
ではどうするのかというと以下のようにします。
package com.tsukaby.scala.sample import scala.collection.JavaConverters._ object Main { def main(args: Array[String]) { val javaList: java.util.List[Int] = new java.util.ArrayList[Int]() printListContents(javaList.asScala.toList) } def printListContents(list: scala.List[Int]) { for (value <- list) { println(f"value=$value%d") } } }
JavaConverters._をimportすることでasScalaが使えるようになります。これで変換が可能です。asScalaではBuffer型になるため、ここではtoListしてscala.Listにしています。
scala.Listからjava.util.Listへの変換
これも同じです。JavaConverters様々ですね。
asJavaで変換できています。
package com.tsukaby.scala.sample import scala.collection.JavaConverters._ object Main { def main(args: Array[String]) { val scalaList: scala.List[Int] = scala.List(1, 2, 3, 4) printListContents(scalaList.asJava) } def printListContents(list: java.util.List[Int]) { for(i <- 0 until list.size()){ val value = list.get(i) println(f"value=$value%d") } } }
scala.Listから配列への変換
まず以下のような配列を標準出力に吐き出すJavaコード(ライブラリ)があったとします。
package com.tsukaby.scala.sample; public enum HogeUtil { ; public static void printArray(int[] array) { for (int i : array) { System.out.println(String.format("value=%d", i)); } } }
scala.Listは型が違いのでそのままではprintArrayに与えてやることはできません。
これは簡単でtoArrayするだけです。
package com.tsukaby.scala.sample object Main { def main(args: Array[String]) { val scalaList: scala.List[Int] = List(1, 2, 3, 4) HogeUtil.printArray(scalaList.toArray) } }
java.util.Listから配列への変換
少しだけトリッキーですが、コード自体は全然大したこと無いです。
package com.tsukaby.scala.sample import java.util import collection.JavaConverters._ object Main { def main(args: Array[String]) { val javaList: java.util.List[Int] = new util.ArrayList() javaList.add(1) javaList.add(2) javaList.add(3) javaList.add(4) // NG Array[AnyRef] //HogeUtil.printArray(javaList.toArray) // Compile OK, but Runtime NG //HogeUtil.printArray(javaList.toArray(new Array[Int](0))) // OK HogeUtil.printArray(javaList.asScala.toArray) } }
例によってJavaConvertersをimportした上でasScalaし、toArrayすればOKです。
javaList.toArrayのようにするとArray[AnyRef]が帰ってくるため、コンパイルが通りません。
javaList.toArray(new Array[Int](0))のようにするとコンパイルは通りますが、実行するとエラーになります。
Error:(20, 34) overloaded method value toArray with alternatives: [T](x$1: Array[T with Object])Array[T with Object] <and> ()Array[Object] cannot be applied to (Array[Int]) HogeUtil.printArray(javaList.toArray(new Array[Int](0))) ^
詳しく調べてないので予想ですが、Javaで言うInteger(intのラッパークラス)の配列に変換されているからNGなのかな、と。
配列からscala.List, java.util.Listへの変換
package com.tsukaby.scala.sample import collection.JavaConversions._ import collection.JavaConverters._ object Main { def main(args: Array[String]) { val arr: Array[Int] = Array(1, 2, 3, 4) val javaList = arr.toList.asJava for(value <- javaList){ println(f"value=$value") } val scalaList = arr.toList for(value <- javaList){ println(f"value=$value") } } }
普通にtoListとasJavaを使うだけですね。簡単!
やり方さえ分かってしまえばどうということは無いですが、若干面倒ですね。暗黙変換はまあ楽と言えば楽ですが。できれば使用するライブラリはscalaのもので揃えたいところですね。
以上、JavaおよびScalaのList, Arrayの使い方でした!