如果您在单个语句中执行某些操作,例如“abc”+ stringval +“abc”,那么是一个不可变的字符串副本,或者两个(注意到abc和123在编译时是常量)
Bonus round:使用像下面这样的StringBuilder会有多少开销?
def surround(s:String, ss:String):String = {
val surrounded = new StringBuilder(s.length() + 2*ss.length(), s)
surrounded.insert(0,ss)
surrounded.append(ss)
surrounded.mkString
}
还是有一种我不知道的更惯用的方式吗?
答案 0 :(得分:6)
它比连接的开销少。但是示例中的插入效率不高。以下是一个更清洁,只使用追加效率。
def surround(s:String, ss:String) =
new StringBuilder(s.length() + 2*ss.length(), ss).append(s).append(ss).mkString
答案 1 :(得分:3)
我的第一个冲动是查看字节码并查看。所以,
// test.scala
object Comparison {
def surround1(s: String, ss: String) = {
val surrounded = new StringBuilder(s.length() + 2*ss.length(), s)
surrounded.insert(0, ss)
surrounded.append(ss)
surrounded.mkString
}
def surround2(s: String, ss: String) = ss + s + ss
def surround3(s: String, ss: String) = // Neil Essy
new StringBuilder(s.length() + 2*ss.length(), ss).append(s).append(ss).mkString
}
然后:
$ scalac -optimize test.scala
$ javap -verbose Comparison$
[... lots of output ...]
粗略地说,Neil Essy和你的相同但是对于一个方法调用(和一些堆栈噪声)。 surround2
被编译成类似
val sb = new StringBuilder()
sb.append(ss)
sb.append(s)
sb.append(ss)
sb.toString
我是Scala(和Java)的新手,所以我不知道查看字节码有多普遍有用 - 但是它会告诉你这个 scalac
使用此代码。
答案 2 :(得分:2)
在Java中进行一些测试,现在在Scala中使用StringBuilder的值是值得怀疑的,除非你做了很多非常量字符串的附加。
object AppendTimeTest {
val tries = 500000
def surround(s:String, ss:String) = {
(1 to tries).foreach(_ => {
new StringBuilder(s.length() + 2*ss.length(), ss).append(s).append(ss).mkString
})
val start = System.currentTimeMillis()
(1 to tries).foreach(_ => {
new StringBuilder(s.length() + 2*ss.length(), ss).append(s).append(ss).mkString
})
val stop = System.currentTimeMillis()
val delta:Double = stop -start
println("Total time: " + delta + ".\n Avg. time: " + (delta/tries))
}
def surroundStatic(s:String) = {
(1 to tries).foreach(_ => {
"ABC" + s + "ABC"
})
val start = System.currentTimeMillis()
(1 to tries).foreach(_ => {
"ABC" + s + "ABC"
})
val stop = System.currentTimeMillis()
val delta:Double = stop -start
println("Total time: " + delta + ".\n Avg. time: " + (delta/tries))
}
}
在解释器中调用几次会产生:
scala> AppendTimeTest.surroundStatic("foo")
Total time: 241.0.
Avg. time: 4.82E-4
scala> AppendTimeTest.surround("foo", "ABC")
Total time: 222.0.
Avg. time: 4.44E-4
scala> AppendTimeTest.surroundStatic("foo")
Total time: 231.0.
Avg. time: 4.62E-4
scala> AppendTimeTest.surround("foo", "ABC")
Total time: 247.0.
Avg. time: 4.94E-4
因此,除非你附加许多不同的非常量字符串,否则我相信你不会看到性能上的任何重大差异。 Alsol连接常量(即"ABC" + "foo" + "ABC"
)就像编译器所知道的那样(这至少是Java中的情况,但我相信Scala也适用)
答案 3 :(得分:1)
Scala非常接近java进行字符串操作。你的例子:
val stringval = "bar"
"abc" + stringval + "abc"
实际上以(在java风格中)结束为:
(new StringBuilder()).append("abc").append(stringval()).append("abc").toString()
这与java的行为相同,字符串之间的+通常会转换为StringBuilder的实例,效率更高。所以在这里,你正在为StringBuilder做三个副本,并创建一个最终的String,并且根据字符串的长度,可能有三个重新分配。
在你的例子中:
def surround(s:String, ss:String):String = {
val surrounded = new StringBuilder(s.length() + 2*ss.length(), s)
surrounded.insert(0,ss)
surrounded.append(ss)
surrounded.mkString
}
你的副本数量相同(3),但你只分配一次。
建议:如果字符串很小,使用+,它会产生很小的差异。如果字符串相对较大,则使用相关长度初始化StringBuilder,然后简单地追加。这对其他开发者来说更加清晰。
与性能一样,测量它,如果差异很小,请使用更简单的解决方案
答案 4 :(得分:1)
通常在Java / Scala中,源代码中的String
文字是 interned 以提高效率,这意味着代码中的所有副本都将引用同一个对象。所以只有一个“副本”“abc”。
Java和Scala没有像C ++那样的“常量”。用val
初始化的变量是不可变的,但在一般情况下,从一个实例到下一个实例的val不相同(通过构造函数指定)。
因此理论上编译器可以检查val将始终初始化相同值并进行相应优化的简单情况,但这会增加额外的复杂性。您可以通过将其写为“abc123abc”来自行优化。
其他人已经解决了你的奖金问题。
答案 5 :(得分:0)
您也可以在 //Returns 5 children
cy.get('ul').children().toArray().reverse().forEach($el) => {
$($el).click();
});
上使用 mkString
。
String
在内部它也使用 def surround(s:String, ss:String) = s.mkString(ss, "", ss)
,但我喜欢它的读取方式。
我什至可能会内联它。