当前位置:  开发笔记 > 编程语言 > 正文

Scala将Collection转换为按键映射的最佳方法是什么?

如何解决《Scala将Collection转换为按键映射的最佳方法是什么?》经验,为你挑选了6个好方法。

如果我有一个集合c型的T,有一个属性pT(类型P,说的),什么是做一个最好的办法地图通过提取键

val c: Collection[T]
val m: Map[P, T]

一种方法如下:

m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }

但现在我需要一个可变的地图.有没有更好的方法来做到这一点,以便它在一行,我最终得到一个不可变的地图?(显然我可以将上面的内容变成一个简单的库实用程序,就像在Java中一样,但我怀疑在Scala中没有必要)



1> Ben Lings..:

您可以使用

c map (t => t.getP -> t) toMap

但请注意,这需要2次遍历.


在我的机器上有一个包含500,000个元素的列表,这个Scala代码比直接Java方法慢20倍(创建具有适当大小的HashMap,循环列表,将元素放入映射).对于5,000个元素,Scala大约慢8倍.用Scala编写的循环方法大约比toMap变种快3倍,但仍然比Java慢2到7倍.
我仍然更喜欢我在Trabsable [K] .mapTo(K => V)`和`Traversable [V] .mapBy(V => K)`的trac中的建议更好!
将`c`替换为`c.iterator`以避免创建中间集合.
请注意,这是一个二次运算,但对于此处给出的大多数其他变体也是如此.查看scala.collection.mutable.MapBuilder等的源代码,在我看来,对于每个元组,都会创建一个新的不可变映射,其中添加了元组.
您能否向SO社区提供测试来源?谢谢.

2> James Iry..:

您可以使用可变数量的元组构造Map.因此,使用集合上的map方法将其转换为元组集合,然后使用:_*trick将结果转换为变量参数.

scala> val list = List("this", "maps", "string", "to", "length") map {s => (s, s.length)}
list: List[(java.lang.String, Int)] = List((this,4), (maps,4), (string,6), (to,2), (length,6))

scala> val list = List("this", "is", "a", "bunch", "of", "strings")
list: List[java.lang.String] = List(this, is, a, bunch, of, strings)

scala> val string2Length = Map(list map {s => (s, s.length)} : _*)
string2Length: scala.collection.immutable.Map[java.lang.String,Int] = Map(strings -> 7, of -> 2, bunch -> 5, a -> 1, is -> 2, this -> 4)


我一直在阅读有关Scala的信息超过2周,并通过示例进行操作,而不是曾经看过这个":_*"符号!非常感谢您的帮助

3> Daniel Spiew..:

除了@James Iry的解决方案之外,还可以使用折叠来实现此目的.我怀疑这个解决方案比tuple方法稍快(创建的垃圾对象更少):

val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }


@Daniel我尝试你的代码,但出现以下错误:"value update不是scala.collection.immutable.Map [String,Int]的成员".请解释您的代码如何使用此代码?
不可变版本:`list.foldLeft(Map [String,Int]()){(m,s)=> m +(s - > s.length)}`.请注意,如果要使用逗号构建元组,则需要一对额外的括号:`((s,s.length))`.

4> 小智..:

这可以通过如下折叠整个集合而不变地实现并且通过单次遍历来实现.

val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) }

解决方案有效,因为添加到不可变Map会返回带有附加条目的新不可变Map,并且此值通过fold操作用作累加器.

这里的权衡是代码与其效率的简单性.因此,对于大型集合,这种方法可能比使用2遍遍实现更合适,例如应用maptoMap.



5> Somatik..:

另一种解决方案(可能不适用于所有类型)

import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)

这避免了中间列表的创建,更多信息在这里: Scala 2.8 breakOut



6> Eyal Roth..:

你想要实现的是有点不确定.
如果两个或多个项目c共享相同p怎么办?哪个项目将映射到p地图中的项目?

更准确的方式是在它之间产生一个地图p和所有c拥有它的项目:

val m: Map[P, Collection[T]]

这可以通过groupBy轻松实现:

val m: Map[P, Collection[T]] = c.groupBy(t => t.p)

如果你仍然想要原始地图,你可以,例如,映射p到第一个地图t:

val m: Map[P, T] = c.groupBy(t => t.p) map { case (p, ts) =>  p -> ts.head }

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有