1

如果你喜歡「ABC」 + stringval +「ABC」一條語句做的事,是一個不可改變的字符串拷貝,或兩個(注意,ABC和123是在編譯時間常數)周邊斯卡拉字符串

獎金回合:使用類似以下的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 
    } 

或者還有一種我不知道的更習慣的方式?

回答

6

它有比串聯更少的開銷。但是在你的例子中插入是沒有效率的。以下是一些簡單的清潔工具,僅用於追求效率。

def surround(s:String, ss:String) = 
    new StringBuilder(s.length() + 2*ss.length(), ss).append(s).append(ss).mkString 
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 ...] 

粗略地說,尼爾·埃西的和你是相同的,但對於一個方法調用(和一些堆棧噪聲)。 surround2被編譯成類似

val sb = new StringBuilder() 
sb.append(ss) 
sb.append(s) 
sb.append(ss) 
sb.toString 

我是新來斯卡拉(和Java),所以我不知道它是如何通常是有用的看字節碼 - 但它告訴你什麼這個scalac這個代碼。

+2

surround2與surround3之間的主要區別在於,surround3預先計算了支持StringBuilder所需的數組大小。這消除了擴展支持構建器的數組的可能性和成本,其細節隱藏在StringBuilder類中。 –

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)

+0

您應該嘗試使用總共16個字符以上的最終字符串長度,因爲最初StringBuilder爲0。 –

+0

我無恥地偷了@Neil Essy的追加代碼:)。如果我用一個零參數替換StringBuilder並使用append添加字符串,我會得到類似的結果。也嘗試增加靜態字符串一點,即「ABCDEFGHIJKLMNOPQRSTUVXYZ」 –

+0

@Luigi我意識到你的意思是嘗試使用相同的代碼,但字符串超過16個字符,而不是將StringBuilder更改爲零參數(因爲這是無論如何,在surroundStatic方法中幕後會發生什麼)......這樣做實際上會產生一些速度差,對於SurroundStatic方法,速度大約慢了1.6倍。 –

1

Scala與Java的字符串操作非常接近。你的榜樣:

val stringval = "bar" 
"abc" + stringval + "abc" 

實際上結束了(在Java的方式)爲:

(new StringBuilder()).append("abc").append(stringval()).append("abc").toString() 

這是相同的行爲如Java,+字符串之間通常被翻譯爲StringBuilder的實例,這是更有效。所以在這裏,你要爲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 
} 

你正在做同樣的份數(3),但你只能分配一次。

建議:如果字符串很小,請使用+,這樣做幾乎沒有什麼區別。如果字符串比較大,則用相應的長度初始化StringBuilder,然後簡單追加。對於其他開發者來說這更清晰。

由於總是與性能,測量它,並且如果差異很小,在Java/Scala中使用簡單的解決方案

1

通常,String文字在源代碼中實習爲了效率,這意味着所有副本它在你的代碼中會引用同一個對象。所以只會有一個「abc」的「副本」。

Java和Scala沒有像C++中的「常量」。使用val初始化的變量是不可變的,但在一般情況下,從一個實例到下一個實例的vals不相同(通過構造函數指定)。

所以在理論上,編譯器可以檢查val總是初始化相同的值並相應地進行優化的簡單情況,但這會增加額外的複雜性。你可以通過寫成「abc123abc」來自己優化它。

其他人已經解決了您的獎金問題。