Scala: возможна ли HashMap с разными типами данных для разных ключей? - PullRequest
1 голос
/ 30 марта 2011

Я совершенно новичок в программировании на Scala, и у меня возникла следующая проблема:

Мне нужен HashMap, который может содержать много типов данных (Int, String и т. Д.).В C ++ я бы использовал MultiMap от BOOST.Я слышал, что в Scala есть черта MultiMap.В основном я хочу следующее:

val map = HashMap[String, ListBuffer[_]]

Конкретный тип данных элементов ListBuffer должен быть определен во время выполнения.Когда я тестирую эту реализацию в консоли, работает следующее:

scala> val a = new HashMap[String, ListBuffer[_]]()
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.ListBuffer[_]] = Map()

scala> val b = new ListBuffer[String]()
b: scala.collection.mutable.ListBuffer[String] = ListBuffer()

scala> val c = new ListBuffer[Int]()
c: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> b += "String"
res0: b.type = ListBuffer(String)

scala> c += 1
res1: c.type = ListBuffer(1)

scala> a += "String Buffer" -> b
res2: a.type = Map((String Buffer,ListBuffer(String)))

scala> a += "This is an Int Buffer" -> c
res3: a.type = Map((String Buffer,ListBuffer(String)), (This is an Int Buffer,ListBuffer(1)))

Так что в основном это работает.Мой первый вопрос: есть ли возможность реализовать такое же поведение в Scala без использования ListBuffer в качестве слоя косвенности.

Например, получение карты со следующим содержимым: Map ((String, 1), (String, "String value"), ...)

Когда я сейчас пытаюсь использовать реализацию ListBuffer-выше, я получаю следующую ошибку несоответствия типов:

found   : _$1 where type _$1
required: _$3 where type _$3

Яв основном пытаюсь сделать следующее:

Я использую итератор для перебора ключей карты:

var valueIds = new ListBuffer[Int]()
val iterator = map.keys
iterator.foreach(key => { valueIds += setValue((map.apply(key)).last) }

setValue возвращает Int и является методом, который должен что-то делать с последним элементом ListBuffers.

Кто-нибудь знает, как исправить вышеупомянутое несоответствие типов?

Спасибо за вашу помощь!

С уважением

Ответы [ 4 ]

6 голосов
/ 30 марта 2011

Вам нужно хранить несколько значений для каждого ключа?Если это так, то использование ListBuffer или другой коллекции необходимо в любом случае.(Scala хранит наборы MultiMap s, поэтому, если вам нужно хранить дубликаты, они не будут работать.)

Если вам не нужно хранить несколько значений для каждого ключа, вам просто нужен тип Any:

scala> val map = collection.mutable.HashMap[String,Any]()
map: scala.collection.mutable.HashMap[String,Any] = Map()

scala> map += "One" -> 1
res1: map.type = Map((One,1))

scala> map += "Two" -> "ii"
res2: map.type = Map((Two,ii), (One,1))

scala> map += "Three" -> None
res3: map.type = Map((Three,None), (Two,ii), (One,1))

, где теперь вам, вероятно, потребуется выполнить сопоставление с шаблоном или использовать метод сбора, чтобы сделать что-нибудь полезное для значений:

scala> map.values.foreach(_ match { case i: Int => println("We stored the number "+i) })
We stored the number 1

scala> map.values.collect{ case i: Int => i }
res4: Iterable[Int] = List(1)
4 голосов
/ 30 марта 2011

Scala имеет MultiMap класс

scala> import scala.collection.mutable.{HashMap, MultiMap, Set}     
import scala.collection.mutable.{HashMap, MultiMap, Set}

scala> val a = new HashMap[String, Set[Any]] with MultiMap[String, Any]
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Any]] with scala.collection.mutable.MultiMap[String,Any] = Map()

scala> a.addBinding("Pants", 1)
res0: a.type = Map((Pants,Set(1)))

scala> a.addBinding("Pants", 2)
res1: a.type = Map((Pants,Set(1, 2)))

scala> a.addBinding("Trousers", 3)
res2: a.type = Map((Trousers,Set(3)), (Pants,Set(1, 2)))

scala> a.mapValues { v => v.last }
res3: scala.collection.Map[String,Any] = Map((Trousers,3), (Pants,2))

scala> val valueIds = a.values.flatten
valueIds: Iterable[Any] = List(3, 1, 2)

Я думаю, что это имеет смысл, когда вы смотрите на ваш код относительно того, что вы хотите.

1 голос
/ 30 марта 2011

Если вы используете заполнитель _ в качестве параметра типа, он преобразуется в экзистенциальный тип, т. Е. Ваше определение ListBuffer[_] становится ListBuffer[A] forSome { type A }. Это означает, что компилятор ничего не знает об этом типе A и не может делать никаких предположений относительно него.

Самым простым решением было бы просто использовать ListBuffer[Any] и обернуть карту примерно так:

val m = new HashMap[String,ListBuffer[Any]]

def get(key: String) =
  m.getOrElseUpdate(key, new ListBuffer())

get("Strings") += "a"
get("Strings") += "b" += "c"
get("Ints") += 1 += 2
// m is now:
// Map(Ints -> ListBuffer(1, 2), Strings -> ListBuffer(a, b, c))
0 голосов
/ 26 марта 2012

Позвольте мне предложить еще один способ:

import scala.collection.mutable

val map = mutable.Map.empty[String, Vector[Any]].withDefaultValue(Vector.empty)
map("strings") :+= "one"
map("ints") :+= 1
map("ints") ++= Seq(1, 2)

assert {
  map("default") == Vector.empty && // no side effects here
  !map.contains("default")
}
...