いつも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
というわけで、気をつけましょう。