Scala:HashMap 可以针对不同的键使用不同的数据类型吗?

发布于 2024-10-28 06:20:17 字数 1607 浏览 1 评论 0原文

我对 Scala 编程完全陌生,并且遇到以下问题:

我需要一个可以包含许多数据类型(Int、String 等)的 HashMap。在 C++ 中我会使用 BOOST 的 MultiMap。我听说 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: Map((String, 1),(String, "String value"), ...)

当我现在尝试使用上面的 ListBuffer-Implementation 时,出现以下类型不匹配错误:

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 最后一个元素执行某些操作的方法。

有谁知道如何解决上述类型不匹配的问题?

感谢您的帮助!

问候

I am totally new to programming with Scala and I have the following problem:

I need a HashMap which can contain many data types (Int, String, etc..). In C++ I would use BOOST's MultiMap. I have heard that in Scala there is a MultiMap trait available. Basically what I want to to is the following:

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

The concrete datatype of the ListBuffer's elements are to be determined during runtime. When I test this implementation in console the following works:

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)))

So basically it works. My first question is, whether there is a possiblity to implement the same behaviour in Scala without using the ListBuffer as layer of indirection.

E.g getting a Map with the following content: Map((String, 1),(String, "String value"), ...)

When I am now trying to use the ListBuffer-Implementation above, I get the following type mismatch error:

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

I am basically trying to do the following:

I use an iterator to iterate over the map's keys:

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

setValue returns Int and is a method which has to do something with the ListBuffers last element.

Does anybody know how to fix the above mentioned type mismatch?

Thanks for your help!

Regards

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

影子的影子 2024-11-04 06:20:17

您需要为每个键存储多个值吗?如果是这样,无论如何都需要使用 ListBuffer 或其他集合。 (Scala 的 MultiMap 存储集,因此如果您需要保存重复项,它们将不起作用。)

如果您不需要每个键存储多个值,那么您只需要 Any type:

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)

Do you need multiple values stored per key? If so, using a ListBuffer or other collection is necessary anyway. (Scala's MultiMaps store sets, so if you need to hold duplicates they won't work.)

If you do not need multiple values stored per key, then you just want the Any type:

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))

where you'll now presumably need to do pattern matching or use collect to do anything useful to the values:

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)
烟酒忠诚 2024-11-04 06:20:17

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)

我认为这使得当你查看你的代码时就知道你想要什么。

Scala has a MultiMap class

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)

I think that makes sense when looking at your code as to what you want.

醉生梦死 2024-11-04 06:20:17

如果您使用占位符 _ 作为类型参数,则会将其转换为存在类型,即您的定义 ListBuffer[_] 变为 ListBuffer[A] forSome { type一个}。这意味着编译器不知道有关该类型 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))

If you use the placeholder _ as a type parameter this gets translated into an existential type, i.e. your definition ListBuffer[_] becomes ListBuffer[A] forSome { type A }. This means that the compiler does not know anything about that type A and cannot make any assumptions about it.

The easiest fix would be to simply use a ListBuffer[Any] and wrap the map with something like this:

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))
孤星 2024-11-04 06:20:17

让我建议另一种方法:

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")
}

Let me suggest one more way:

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")
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文