go字符串高效拼接

字符串高效拼接

Go語言中为牍,字符串(string)是不可變的辆亏,拼接字符串事實(shí)上是創(chuàng)建了一個新字符串對象蛾狗。
如果代碼大量出現(xiàn)字符串拼接,那么代碼性能將會大大折扣座菠。

拼接字符串方式
  • 直接 "+" 拼接

         func plusConcat(n int, str string) string {
             s := ""
             for i := 0; i < n; i++ {
                 s += str
             }
             return s
         }
    
  • 使用 fmt.Sprintf()

         func sprintfConcat(n int, str string) string {
             s := ""
             for i := 0; i < n; i++ {
                 s = fmt.Sprintf("%s%s", s, str)
             }
             return s
         }
    
  • 使用 strings.Builder

         func builderConcat(n int, str string) string {
             var builder strings.Builder
             for i := 0; i < n; i++ {
                 builder.WriteString(str)
             }
             return builder.String()
         }
    
  • 使用 bytes.Buffer

         func bufferConcat(n int, s string) string {
             buf := new(bytes.Buffer)
             for i := 0; i < n; i++ {
                 buf.WriteString(s)
             }
             return buf.String()
         }
    
  • 使用 []byte

         func byteConcat(n int, str string) string {
             buf := make([]byte, 0)
             for i := 0; i < n; i++ {
                 buf = append(buf, str...)
             }
             return string(buf)
         }
    
  • 使用 strings.Builder, 且預(yù)分配切片的容量(cap)

         func builderConcat(n int, str string) string {
             var builder strings.Builder
             builder.Grow(n * len(str))
             for i := 0; i < n; i++ {
                 builder.WriteString(str)
             }
             return builder.String()
         }
    
  • 使用 []byte, 且預(yù)分配切片的容量(cap)

         func preByteConcat(n int, str string) string {
             buf := make([]byte, 0, n*len(str))
             for i := 0; i < n; i++ {
                 buf = append(buf, str...)
             }
             return string(buf)
         }
    
比較效率
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randomString(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Intn(len(letterBytes))]
    }
    return string(b)
}

func benchmark(b *testing.B, f func(int, string) string) {
    var str = randomString(10)
    for i := 0; i < b.N; i++ {
        f(10000, str)
    }
}

func BenchmarkPlusConcat(b *testing.B)       { benchmark(b, plusConcat) }
func BenchmarkSprintfConcat(b *testing.B)    { benchmark(b, sprintfConcat) }
func BenchmarkBuilderConcat(b *testing.B)    { benchmark(b, builderConcat) }
func BenchmarkBufferConcat(b *testing.B)     { benchmark(b, bufferConcat) }
func BenchmarkByteConcat(b *testing.B)       { benchmark(b, byteConcat) }
func BenchmarkPreBuilderConcat(b *testing.B) { benchmark(b, preBuilderConcat) }
func BenchmarkPreByteConcat(b *testing.B)    { benchmark(b, preByteConcat) }

##### 執(zhí)行 `go test -bench="Concat$" -benchmem .`
    goos: windows
    goarch: amd64
    pkg: hello
    cpu: Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz
    BenchmarkPlusConcat-12                        13          88328838 ns/op        530999171 B/op      10038 allocs/op
    BenchmarkSprintfConcat-12                      7         154641671 ns/op        832944258 B/op      34091 allocs/op
    BenchmarkBuilderConcat-12                   9126            132454 ns/op           514803 B/op         23 allocs/op
    BenchmarkBufferConcat-12                    9463            115917 ns/op           368577 B/op         13 allocs/op
    BenchmarkByteConcat-12                      8782            144326 ns/op           621299 B/op         24 allocs/op
    BenchmarkPreBuilderConcat-12               24392             50559 ns/op           106496 B/op          1 allocs/op
    BenchmarkPreByteConcat-12                  20532             60857 ns/op           212993 B/op          2 allocs/op
    PASS
  • 直接使用 '+' 拼接效率很低狸眼,且分配內(nèi)存次數(shù)高達(dá)10038次,占用高達(dá)530999171 B浴滴。
    執(zhí)行 '+' 拼接10000次拓萌,且每次都需要給新字符串申請內(nèi)存。

  • 使用 fmt.Sprintf() 效率比 直接使用 '+' 拼接更低升略,因?yàn)橥ǔ?fmt.Sprintf() 是用來格式化字符串的

  • 使用 strings.Builder 微王、bytes.Buffer[]byte 效率相似品嚣,且效率遠(yuǎn)大于直接使用 '+' 拼接字符串炕倘。
    使用 strings.Builder 與 使用 '+' 的主要區(qū)別在于內(nèi)存分配次數(shù)。

    • 使用 '+' 拼接字符串翰撑,拼接n次就要申請n次內(nèi)存罩旋,申請內(nèi)存大小(B)=10 + 210 + 310 + ... + 10000*10
    • 使用 strings.Builder 拼接字符串,拼接n次需要內(nèi)存>10*10000眶诈,申請內(nèi)存大小(B)=16 + 32 + 64 + ... + 122880
  • 使用 strings.Builder 且預(yù)分配內(nèi)存大小涨醋,效率則會比不預(yù)分配內(nèi)存翻倍, 例如 preBuilderConcat

  • preBuilderConcatpreByteConcat 少一次內(nèi)存分配是因?yàn)?bytes.Buffer 轉(zhuǎn)化為字符串時重新申請了一塊空間册养,存放生成的字符串變量东帅,而 strings.Builder 直接將底層的 []byte 轉(zhuǎn)換成了字符串類型返回了回來压固。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末球拦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帐我,更是在濱河造成了極大的恐慌坎炼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦键,死亡現(xiàn)場離奇詭異谣光,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芬为,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門萄金,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蟀悦,“玉大人,你說我怎么就攤上這事氧敢∪崭辏” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵孙乖,是天一觀的道長浙炼。 經(jīng)常有香客問我,道長唯袄,這世上最難降的妖魔是什么弯屈? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮恋拷,結(jié)果婚禮上资厉,老公的妹妹穿的比我還像新娘。我一直安慰自己蔬顾,他們只是感情好酌住,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阎抒,像睡著了一般酪我。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上且叁,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天都哭,我揣著相機(jī)與錄音,去河邊找鬼逞带。 笑死欺矫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的展氓。 我是一名探鬼主播穆趴,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遇汞!你這毒婦竟也來了未妹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤空入,失蹤者是張志新(化名)和其女友劉穎络它,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歪赢,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡化戳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了埋凯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片点楼。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡扫尖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出掠廓,到底是詐尸還是另有隱情藏斩,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布却盘,位于F島的核電站狰域,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏黄橘。R本人自食惡果不足惜兆览,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塞关。 院中可真熱鬧抬探,春花似錦、人聲如沸帆赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椰于。三九已至怠益,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘾婿,已是汗流浹背蜻牢。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偏陪,地道東北人抢呆。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像笛谦,于是被迫代替她去往敵國和親抱虐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容