Как заполнить java.util.HashMap на лету из кода Scala? - PullRequest
12 голосов
/ 02 октября 2010

Я тестирую Java-код из ScalaTest и хотел бы заполнить java.util.HashMap в том же заявлении, которое он объявил Возможно ли это сделать в Scala?

Ответы [ 5 ]

25 голосов
/ 02 октября 2010

Существует множество способов сделать это, только некоторые из которых появились в ответах.

Метод первый: Поскольку 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, но он более компактен, чем версия присвоения из кода блока, поскольку вам не нужно назначать новую карту для 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, для которого требуется карта). Смешивайте и сочетайте по своему усмотрению.

8 голосов
/ 02 октября 2010

Вы можете сделать карту как анонимный класс и выполнить инициализацию как часть инициализации экземпляра объекта.

import java.util.HashMap
val jhm = new HashMap[String, Int](){
   put(key1, value1)
   put(key2, value2)
}

Это на самом деле одинаково хорошо работает в Java (за исключением необходимости использовать двойные скобки){{}}), но гораздо более идиоматичен в Scala.

3 голосов
/ 02 октября 2010

Опираясь на ответ Рэндалла, вы можете использовать JavaConversions, чтобы немного помочь.

import collection.JavaConversions.asMap
import java.util.HashMap
val jhm = new HashMap[Int,String](Map(1->"one", 2->"two"))
0 голосов
/ 02 октября 2010

Чтобы сделать что-то многократно используемое, можно было бы создать новый подтип "Карта" только для синтаксиса инициализации.

Это могло бы работать примерно так (я игнорирую обобщения, потому что я не использую их регулярнои я, вероятно, пойму что-то не так):

HashMap hm=new HashMap(
    new InitMap(
        new String[]{"one", "two", "three"},
        new int[]   {  1  ,   2  ,    3   };
    )
);

В классе InitMap будет задействовано больше кода, но он будет многоразовым и достаточно простым (мне действительно нравится синтаксис инициализации массива для такого родавещи).

Думая об этом, класс InitMap не будет слишком сложным.Возможно, вы захотите выяснить, какие методы были вызваны, и просто реализовать их.Скорее всего, он вызовет только методы KeySet и EntrySet.

Конечно, при такой скорости вы можете просто создать вспомогательный метод, который будет принимать два массива и возвращать HashMap или расширять HashMap и добавлять новый конструктор ...

0 голосов
/ 02 октября 2010

Все методы и конструкторы java.util.HashMap, конечно, доступны для вас, но это не обеспечивает способ инициализации карты, если у вас нет другого, чтобы предоставить начальные значения. Самое близкое, что вы, вероятно, собираетесь получить:

import java.util.HashMap
val jhm = new HashMap[String, Int]
«code to add key-value pairs to jhm»
...