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の使い方でした!