字符串插值与字符串拼接

21

在Scala中,使用字符串插值比字符串连接更高效吗?例如:

val myVal = "def"
val test = s"abs${myVal}ghi"

对比

val test = "abc" + myVal + "ghi"

我之所以问这个问题,是因为我需要为一个class编写toString方法,该class很多参数,因此插值字符串变得非常长和混乱,我想将其拆分成多行,但担心如果我连接字符串会导致性能变差。

1个回答

16

我做了一个简单的示例:

class Employee(name: String, age: Int) {
  private var s = 0.0
  def salary = s
  def salary_=(s: Double) = this.s = s
  def toStringConcat(): String = {
    "Name: " + name + ", age: " + age + ", salary: " + salary
  }
  def toStringInterpol(): String = {
    s"Name: $name, age: $age, salary: $salary"
  }
}

object Program {
  val empl = new Employee("John", 30)
  empl.salary = 10.50
  val times = 10000000;

  def main(args: Array[String]): Unit = {
    // warming-up
    val resultConcat = empl.toStringConcat
    val resultInterpol = empl.toStringInterpol
    println("Concat -> " + resultConcat)
    println("Interpol -> " + resultInterpol)

    val secondsConcat0 = run(empl.toStringConcat)
    val secondsInterpol0 = run(empl.toStringInterpol)
    val secondsConcat1 = run(empl.toStringConcat)
    val secondsInterpol1 = run(empl.toStringInterpol)

    println("Concat-0: " + secondsConcat0 + "s")
    println("Concat-1: " + secondsConcat1 + "s")
    println("Interpol-0: " + secondsInterpol0 + "s")
    println("Interpol-1: " + secondsInterpol1 + "s")
  }

  def run(call: () => String): Double = {
    val time0 = System.nanoTime()
    var result = ""
    for (i <- 0 until times) {
      result = call()
    }
    val time1 = System.nanoTime()

    val elapsedTime = time1 - time0;
    val seconds = elapsedTime / 1000000000.0;
    seconds
  }
}

结果如下:

Concat -> Name: John, age: 30, salary: 10.5
Interpol -> Name: John, age: 30, salary: 10.5
Concat-0: 2.831298161s
Concat-1: 2.725815448s
Interpol-0: 3.846891864s
Interpol-1: 3.753401004s

插值情况下速度较慢。原因可以在生成的代码中找到:

  public String toStringConcat()
  {
    return new StringBuilder().append("Name: ").append(this.name).append(", age: ").append(BoxesRunTime.boxToInteger(this.age)).append(", salary: ").append(BoxesRunTime.boxToDouble(salary())).toString();
  }

  public String toStringInterpol()
  {
    return new StringContext(Predef..MODULE$.wrapRefArray((Object[])new String[] { "Name: ", ", age: ", ", salary: ", "" })).s(Predef..MODULE$.genericWrapArray(new Object[] { this.name, BoxesRunTime.boxToInteger(this.age), BoxesRunTime.boxToDouble(salary()) }));
  }

我使用的是Scala 2.10.3和IntelliJ IDEA 13。


2
顺便提一下,2.11.2 版本也有类似的结果。 - Paul Draper
2
似乎这取决于环境。在我的机器上(Java7 32位,Scala 2.11.6,iCore 5),我得到了以下结果: Concat-0: 5.319328531秒 Concat-1: 4.447912174秒 Interpol-0: 4.754240423秒 Interpol-1: 4.663513931秒在我的独立测试中,使用JVM预热插值甚至比串联稍微快一点,无论是使用-client还是-server设置。 - Sergio
随着时间的推移,这些值被观察到:Concat->姓名:约翰,年龄:30岁,薪水:10.5 Interpol->姓名:约翰,年龄:30岁,薪水:10.5 Concat-0:1.677385731秒 Concat-1:1.665851276秒 Interpol-0:2.400097927秒 Interpol-1:2.450880231秒 因此Concat获胜。 - Sebastian J.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接