在Scala中预先添加到LinkedHashMap的解决方法?

时间:2012-07-08 19:54:48

标签: scala immutability linkedhashmap

我有一个LinkedHashMap,我一直在以一种典型的方式使用:添加新的键值 成对结束,并按插入顺序访问它们。但是,现在我有了 特殊情况,我需要将对添加到地图的“头部”。我觉得有 LinkedHashMap源中的一些功能用于执行此操作,但它具有私有功能 可访问性。

我有一个解决方案,我在其中创建一个新地图,添加该对,然后添加所有旧映射。 在Java语法中:

newMap.put(newKey, newValue) 
newMap.putAll(this.map)
this.map = newMap

有效。但问题是我需要建立我的主要数据结构 (this.map)一个var而不是一个val。

有人能想到更好的解决方案吗?请注意,我肯定需要快速查找 Map集合提供的功能。预先支出的表现不是 这么大的一笔。

更一般地说,作为一名Scala开发人员,你会为了避免变数而努力 在这样的情况下,假设没有可预见的并发需求? 你会创建自己的LinkedHashMap版本吗?坦白地说,看起来很麻烦。

2 个答案:

答案 0 :(得分:1)

您是否看过LinkedHashMap的代码?该类有一个字段firstEntry,只需快速查看updateLinkedEntries,创建LinkedHashMap的子类就相对容易了,它只添加一个新方法{{1} }和prepend导致您需要的行为,例如(未经测试):

updateLinkedEntriesPrepend

这是一个示例实现我快速拼凑(即未经过全面测试!):

private def updateLinkedEntriesPrepend(e: Entry) {
    if (firstEntry == null) { firstEntry = e; lastEntry = e }
    else {
        val oldFirstEntry = firstEntry
        firstEntry = e
        firstEntry.later = oldFirstEntry
        oldFirstEntry.earlier = e
    }
}

像这样测试:

class MyLinkedHashMap[A, B] extends LinkedHashMap[A,B] {

  def prepend(key: A, value: B): Option[B] = {
    val e = findEntry(key)
    if (e == null) {
      val e = new Entry(key, value)
      addEntry(e)
      updateLinkedEntriesPrepend(e)
      None
    } else {
      // The key already exists, so we might as well call LinkedHashMap#put
      put(key, value)
    }
  }

  private def updateLinkedEntriesPrepend(e: Entry) {
    if (firstEntry == null) { firstEntry = e; lastEntry = e }
    else {
      val oldFirstEntry = firstEntry
      firstEntry = e

      firstEntry.later = oldFirstEntry
      oldFirstEntry.earlier = firstEntry
    }
  }

}

其中,在Scala 2.9.0上(是的,需要更新)结果

object Main {

 def main(args:Array[String]) {
    val x = new MyLinkedHashMap[String, Int]();

    x.prepend("foo", 5)
    x.prepend("bar", 6)
    x.prepend("olol", 12)

    x.foreach(x => println("x:" + x._1 + " y: " + x._2  ));
  }

}

快速基准测试显示扩展内置类和“地图重写”方法之间性能差异的数量级(我使用Debilski在“ExternalMethod”中的答案和我在“BuiltIn”中的答案):

x:olol y: 12
x:bar y: 6
x:foo y: 5 

基准代码:

 benchmark length        us linear runtime
ExternalMethod     10   1218.44 =
ExternalMethod    100   1250.28 =
ExternalMethod   1000  19453.59 =
ExternalMethod  10000 349297.25 ==============================
       BuiltIn     10      3.10 =
       BuiltIn    100      2.48 =
       BuiltIn   1000      2.38 =
       BuiltIn  10000      3.28 =

使用scala benchmarking template

答案 1 :(得分:1)

这可行,但也不是特别好:

import scala.collection.mutable.LinkedHashMap

def prepend[K,V](map: LinkedHashMap[K,V], kv: (K, V)) = {
  val copy = map.toMap
  map.clear
  map += kv
  map ++= copy
}

val map = LinkedHashMap('b -> 2)
prepend(map, 'a -> 1)
map == LinkedHashMap('a -> 1, 'b -> 2)