【问题标题】:How to avoid overhead to pass a Map[Integer, String] where a Map[Number, String] is expected?如何避免在需要 Map[Number, String] 的地方传递 Map[Integer, String] 的开销?
【发布时间】:2023-03-24 06:50:01
【问题描述】:

问题: 我有一个 mutable.Map[Integer, String],我想将它传递给两个方法:

  1. def processNumbers(nums: Map[Number, String])
  2. def processIntegers(nums: mutable.Map[Integer, String])

得到编译错误后,我得到了这个:

val ints: mutable.Map[Integer, String] = mutable.Map.empty[Integer, String]
//init of ints
val nums: Map[Number, String] = ints.toMap[Number, String]
processNumbers(nums)
processIntegers(ints)

通过一些小实验,我发现我的方法有很大的开销:类型转换步骤乘以 10 的执行时间。
总而言之,类型转换只是为了取悦编译器,那么如何在没有任何开销的情况下做到这一点呢?

有关信息,我的实验代码:

package qndTests
import scala.collection.mutable
object TypeTest {
  var hashNums = 0
  var hashIntegers = 0
  def processNumbers(nums: Map[Number, String]): Unit = {
  nums.foreach(num =>{
      hashNums+=num._1.hashCode+num._2.hashCode
    })
  }
  def processNumbers2(nums: mutable.Map[Integer, String]): Unit = {
  nums.foreach(num =>{
      hashNums+=num._1.hashCode+num._2.hashCode
    })
  }
  def processIntegers(nums: mutable.Map[Integer, String]): Unit = {
  nums.foreach(num =>{
      hashIntegers+=num._1.hashCode+num._2.hashCode
    })
  }
  def test(ints: mutable.Map[Integer, String], convertType: Boolean): Unit = {
  if(convertType)
    println("run test with type conversion")
  else
    println("run test without type conversion")

  val start = System.nanoTime
  hashNums = 0
  hashIntegers = 0
  val nTest = 10
  for(i <- 0 to nTest) {
    if(convertType){
      val nums: Map[Number, String] = ints.toMap[Number, String] //how much does that cost ?
      processNumbers(nums)
    }else{
      processNumbers2(ints)
    }

    processIntegers(ints)
  }
  val end= System.nanoTime
  println("nums: "+hashNums)
  println("ints: "+hashIntegers)
  println(end-start)
}
def main(args: Array[String]): Unit = {
  val ints: mutable.Map[Integer, String] = mutable.Map.empty[Integer, String]
  val testSize = 1000000

  println("creating a map of "+testSize+" elements")
  for(i <- 0 to testSize) ints.put(i, i.toBinaryString)
  println("done")

  test(ints, false)
  test(ints, true)
  }
}

及其输出:

创建一个包含 1000000 个元素的地图
完成
无需类型转换即可运行测试
数字:-1650117013
整数:-1650117013
2097538520
使用类型转换运行测试
数字:-1650117013
整数:-1650117013
25423803480

--> 第一种情况大约 2 秒,而第二种情况大约 25 秒!

【问题讨论】:

    标签: performance scala collections types map


    【解决方案1】:

    如您所见,Map[A,B] 在键类型 A 中是不变的,因此您需要进行某种转换以分配给 Map[A,B]Map[A1,B] 类型的变量,其中 A1 &lt;: A .但是,如果您可以更改 def processNumbers(nums: Map[Number, String]) 的定义,您可以尝试以下操作:

    def processNumbers[T <: Number](nums: Map[T, String])
    

    并传递Map[Integer, String] 而不进行转换。

    这有助于解决您的问题吗?

    【讨论】:

    • 效果很好,谢谢 :-) 如果我们无法更改 processNumbers 的定义(如果它来自库),我们可以再做一个技巧吗?
    • @acapola 我不知道。如果您真的关心性能,并且您确实知道库实际上将其视为 Map[Int, String],即使它设计得很糟糕并且需要 Map[Number, String],那么您可以考虑强制转换你的地图:ints.asInstanceOf[Map[Number, String]],但总的来说,这是一个非常糟糕的主意,只是乞求ClassCastException 在以后的某个地方发生。
    • 非常感谢,这很有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-03
    • 2019-12-06
    • 1970-01-01
    • 1970-01-01
    • 2016-05-21
    • 2017-11-22
    相关资源
    最近更新 更多