Я тестирую код Java с помощью ScalaTest и хотел бы заполнить java.util.HashMap в том же самом заявлении, которое он объявит. Возможно ли это сделать в Scala?
Как заполнить java.util.HashMap на лету от Scala кода?
Ответ 1
Есть несколько способов сделать это, только некоторые из них появились в ответах до сих пор.
Метод первый: Так как java.util.HashMap
имеет конструктор HashMap(Map<? extends K,? extends V> m)
, вы можете передать ему действительную карту Java. И вы можете сделать это тривиально с помощью Scala полезно JavaConversions
:
scala> import scala.collection.JavaConversions._
import scala.collection.JavaConversions._
scala> val myMap = Map(1->"Hi",2->"Bye")
myMap: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,Hi), (2,Bye))
scala> val jmap = new java.util.HashMap[Int,String](myMap) // Need explicit types
jmap: java.util.HashMap[Int,String] = {1=Hi, 2=Bye}
Недостатки здесь в том, что у вас уже есть карта Scala (немного расточительная, если вы просто собираетесь создать Java-вариант), и что вам нужно указать типы. Но он компактный и безболезненный.
Метод второй:. Кроме того, вы можете создать новый блок кода в качестве оператора объявления, поэтому вам даже не нужно иметь JavaConversions
:
scala> val jmap2 = {
| val x = new java.util.HashMap[Int,String]
| for ((k,v) <- List(1->"Howdy",2->"partner")) x.put(k,v)
| x
| }
jmap2: java.util.HashMap[Int,String] = {1=Howdy, 2=partner}
Чуть менее компактный, но полностью общий, а также эффективный (или неэффективный), как вы его делаете.
Метод третий:. Кроме того, вы можете создать анонимный подкласс HashMap, если он будет иметь подкласс (т.е. .getClass
не вернет java.util.HashMap
)), и используйте инициализатор для установки ваших значений:
scala> val jmap3 = new java.util.HashMap[Int,String] {
| put(1,"Yo"); put(2,"bro")
| }
jmap3: java.util.HashMap[Int,String] = {1=Yo, 2=bro}
scala> jmap3.getClass.getName
res0: java.lang.String = $anon$1
scala> jmap3.getClass.getSuperclass.getName
res1: java.lang.String = java.util.HashMap
Недостатком, конечно же, является подкласс HashMap
, а не HashMap
, но он более компактный, чем версия с назначением-из-кода, поскольку вам не нужно назначать новую карту a val.
Метод четыре: И, наконец, вы можете создать метод, который сделает то, что вы хотите, и назовите его:
scala> def newJHM[A,B](kv: Iterable[(A,B)]) = {
| val jhm = new java.util.HashMap[A,B]
| kv.foreach(i => jhm.put(i._1,i._2))
| jhm
| }
newJHM: [A,B](kv: Iterable[(A, B)])java.util.HashMap[A,B]
scala> val jmap4 = newJHM(Seq(1->"Bye",2->"Now")) // Type inference now works
jmap4: java.util.HashMap[Int,java.lang.String] = {1=Bye, 2=Now}
Это едва менее компактно, чем другие, и правильные типы, без необходимости указывать их, поэтому это может быть привлекательным выбором, если вы делаете это более одного раза.
P.S. Просто для удовольствия я показал множество способов получить некоторые пары "ключ-значение" на карте, но они не специфичны для данного метода (кроме # 1, для которого требуется карта). Смешайте и согласитесь по своему усмотрению.
Ответ 2
Вы можете сделать карту как анонимный класс и выполнить инициализацию как часть инициализации экземпляра объекта.
import java.util.HashMap
val jhm = new HashMap[String, Int](){
put(key1, value1)
put(key2, value2)
}
Это действительно одинаково хорошо работает на Java (за исключением требуемых двойных фигурных скобок {{}}), но гораздо более идиоматично в Scala.
Ответ 3
На основе ответа Randall вы можете использовать JavaConversions
, чтобы немного помочь.
import collection.JavaConversions.asMap
import java.util.HashMap
val jhm = new HashMap[Int,String](Map(1->"one", 2->"two"))
Ответ 4
Все методы и конструкторы java.util.HashMap
доступны вам, конечно, но это не дает возможности инициализировать карту, если у вас нет другой, чтобы указать начальные значения. Ближе всего вы, вероятно, получите:
import java.util.HashMap
val jhm = new HashMap[String, Int]
«code to add key-value pairs to jhm»
Ответ 5
Чтобы сделать что-то многоразовое, можно было бы создать новый подтип "Карта" только для синтаксиса инициализации.
Он может работать примерно так (я игнорирую generics, потому что я не использую их регулярно, и я, вероятно, ошибаюсь):
HashMap hm=new HashMap(
new InitMap(
new String[]{"one", "two", "three"},
new int[] { 1 , 2 , 3 };
)
);
В классе InitMap было бы больше кода, но оно было бы многоразовым и довольно прямолинейным (мне очень нравится синтаксис инициализации массива для такого рода материалов).
Размышляя об этом, класс InitMap не будет слишком сложным. Вероятно, вы захотите выяснить, какие методы были вызваны и как их реализовать. Скорее всего, это вызовет только методы KeySet и EntrySet.
Конечно, с такой скоростью вы могли бы просто создать вспомогательный метод, который взял два массива и вернул HashMap или расширил HashMap и добавил новый конструктор...