我有一个案例,我会浏览大量数据并构建多个地图。我的函数的结果将是Maps
:
case class Maps(map1: Map[String, String], map2: Map[String, String])
我试图决定是否使用功能风格或者老式的"建立可变地图的方法。后者看起来大致像
type MutableMap = scala.collection.mutable.Map[String, String]
val MutableMap = scala.collection.mutable.Map
def buildMaps(input: Something): Maps = {
var map1: MutableMap = MutableMap()
var map2: MutableMap = MutableMap()
input.getAnIterator.foreach(x => {
map1 += (key1(x) -> val1(x))
map2 += (key2(x) -> val2(x))
}
Maps(map1.toMap, map2.toMap)
}
我能看到的功能替代方案就像
def addToMaps(maps: Maps, x: SomeElement): Maps =
Maps(maps.map1 + (key1(x) -> val1(x)), maps.map2 + (key2(x) -> val2(x)))
def buildMaps(input: Something): Maps =
input.getAnIterator.foldLeft(Maps(Map(), Map()))(addToMaps)
[我的语法可能不完全正确,但希望这给出了我尝试做的事情的要点。]
第二种方式似乎更多"优雅&#34 ;;但如果通过制作不可变地图的重复副本来实现它,它就不可行(我希望input
非常大)。
Scala能否优化第二种解决方案,使其性能与第一种解决方案相当?我还缺少另一种方法吗?或者我应该坚持使用非功能性方法?
答案 0 :(得分:1)
您还可以对2元素元组的集合执行.toMap
。类似的东西:
def buildMaps(input: Something): Maps = {
val m1 = input.getAnIterator.map(x => key1(x) -> val1(x)).toMap
val m2 = input.getAnIterator.map(x => key2(x) -> val2(x)).toMap
Maps(m1, m2)
}
(假设getAnIterator
返回一个scala迭代器或一些scala集合)
答案 1 :(得分:1)
我想说你方法的第一次实现完全没问题。它还有一个优点,它只使用迭代器一次,并且不会遍历整个集合两次。毕竟,这就是"默认环境机器状态monad"在Scala中最适合:修改可变数据结构。如果不是这种情况,你还会在哪里使用可变变量?函数集合操作的默认实现(如map
,filter
等)无论如何都使用了可变Builder
。
我想引用奥德斯基本人的话: https://www.youtube.com/watch?v=iPitDNUNyR0&t=34m1s。 这是ScalaDays2013的重点说明。大约30分钟左右,Odersky提出了他在小局部范围内使用可变变量的意见。我认为他的观点是:如果可变版本更快,更清晰,并且没有可变状态可以从该方法中逃脱,那么使用可变局部变量就可以了。
我怀疑Scala会自动优化第二个解决方案到第一个,我实际上怀疑第一个解决方案可能会更快一点。但是,您应该对其进行分析,然后才决定这段代码是否值得进行优化。
答案 2 :(得分:1)
正如您所看到的here,Scala的不可变HashMap
实现在有效的恒定时间内进行插入:
所以 - 虽然您对性能的关注并非不切实际,但鉴于此信息我们可以得出结论两个版本的性能可能相当。如果是这样的话 - 我肯定会选择更安全,更简洁的功能风格。
如果您不确定在使用Map()
时实际使用哪种实现,您可以专门实例化new HashMap[String, String]()
以确保这是使用的实现。
答案 3 :(得分:0)
"惯用"答案可能是第三个,除非你有任何一种并发问题,但听起来我们可以把它们排除在外。 Scala的内部集合将集合构建为名为scala.collection.CanBuildFrom
的东西,它为您提供了一个带有不可变result()
方法的可变API。
def buildMaps(input: Something): Maps = {
val builder = Map.newBuilder[A, B]
val m1 = input.getAnIterator.foreach(i => builder += i -> i)
Maps(builder.result())
}
基本上这是引擎盖下使用的模式,所以它使用最低级别的API。
答案 4 :(得分:0)