いつもJavaConvertersでうまくいくとは限らない
ScalaとJavaのコレクションを相互に変換するには scala.collection.JavaConverters
が便利です。例えばJavaのコレクションに .asScala
をつければ、JavaのコレクションをラップしたScalaのコレクションが作られます。ただし、いつも .asScala
で済むとは限りません。コレクションのインターフェイスではなくデータ構造が重要な場合は注意が必要です。
次のコード例は java.util.LinkedHashMap
を .asScala
に変換して、 +=
, +
の操作を行ったものです。 LinkedHashMap
は要素が挿入された順番を保持するはずですが、 +=
を使った場合は順番が保持されるものの、 +
を使った場合は順番が保持されていません。
val m = new java.util.LinkedHashMap[String, Int]() m.put("field1", 1) m.put("field2", 2) import scala.collection.JavaConverters._ val m2 = m.asScala println(m2) // Map(field1 -> 1, field2 -> 2) m2 += "field3" -> 3 m2 += "field4" -> 4 println(m2) // Map(field1 -> 1, field2 -> 2, field3 -> 3, field4 -> 4)
val m = new java.util.LinkedHashMap[String, Int]() m.put("field1", 1) m.put("field2", 2) import scala.collection.JavaConverters._ val m2 = m.asScala println(m2) // Map(field1 -> 1, field2 -> 2) val m3 = m2 + ("field3" -> 3) + ("field4" -> 4) println(m3) // Map(field1 -> 1, field3 -> 3, field2 -> 2, field4 -> 4)
+=
を使った場合、新しい要素は元のLinkedHashMap
に挿入されます。一方、 +
を使った場合は新たな Map
のインスタンスが作られます。その新たな Map
のインスタンスは JavaConverters
で定義してあるラッパークラスが作るのですが、それが LinkedHashMap
ではなく順番を保持しない HashMap
なのです。
これは実際にplay-json 2.6.11に入り込んだバグです。play-jsonはJsonのフィールドを Map
として保持していますが、その Map
をScalaのものから java.util.LinkedHashMap
のインスタンスとして生成して、 JavaConverters
でラップするという実装に変えたところ、Jsonのフィールドの順序が狂うという現象が起きました。
https://github.com/playframework/play-json/issues/236
というわけで、気をつけましょう。