Go 語言中如何操作字符串才是最合適的

先問大家一個問題,平時敲代碼時對於兩個字符串的拼接,最常用的方法是什麼?

在 Go 語言中,字符串的實質就是字節切片,我們也很容易將 string 類型轉換成 []byte(vice-versa),記住這一點之後我們再繼續往下。

首先用一個簡單的字符串的問題開始,這是 exercism.io 上的第二道題,題目非常簡單:給定一個函數:func ShareWith(name string) string,輸入一個字符串,輸出一個字符串,如果輸入的 name 為 Alice,則輸出“One for Alice, one for me.”,如果輸入得字符串為空,那麼輸出“One for you, one for me.”,

這道題非常簡單吧,其實就是一個字符串的拼接問題,當然還有一個字符串的比較。首先說一下這道題的思路(這麼簡單的題也要思路?要!天下難事必作於易,天下大事必作於細。=..=):首先輸入的參數變量名字為 name ,那麼第一步就要判斷 name 是不是為空,如果為空,那麼輸出 “One for you, one for me.”,如果不為空,將 “you” 替換成輸入的數據。還有一種思路就是先判斷 name 是否為空,如果為空,將 name 賦值為 “you”,然後統一將 name 變量的值拼接成結果字符串,然後返回。這兩種思路區別不大,這裡說一個題外話,雖然說這兩種思路差別不大,但是卻能看出來一個人編程的時間有多長,第一種思路的人,基本上都是新手或者編程時間不太長,第二種的對於編程來說應該已經很熟悉了(這不能代表所有人,當然這只是我個人的想法,不喜勿噴=..=),好了,咱們接著說題外話,我為什麼說這兩種思路有差距呢?其實這裡和麵向對象編程有一點點類似,第一種思路,面對的是結果,也就是說,看到了這個函數的功能,就想著輸出正確的結果,當然這也符合人類的思路。但是第二種對於編程來說,就成熟許多了,首先我們看到這個函數的功能,並沒有急著返回正確的結果,而是思考了裡面的實現方式,將程序邏輯進行了整理,然後返回結果。所以我更傾向與第二種,話說回來,基本上編程的人都會是第二種思路的,題外話結束!

對了,附上兩種思路的基本代碼:

Go 語言中如何操作字符串才是最合適的

第一種思路

Go 語言中如何操作字符串才是最合適的

第二種思路

接下來怎麼說一下,這道題大致上有這幾種思路:

  • 直接使用 + 連接
  • 使用 strings 包中的 Join 函數
  • 使用 fmt 包中的 Sprintf 函數
  • 使用 buffer

這裡咱們先討論這四種方法,其他的方法這裡先不做討論。那麼我們先從幾方面來討論:

書寫方便,代碼量

從這一方面,我們主要看的是代碼量最少的,很顯然,直接使用 + 是最少的,然後是 fmt.Sprintf,再就是 strings.Join,代碼量最多的是 buffer。具體代碼這裡就不寫了,大家如果不相信,可以自己試一下=。。=

可讀性

從可讀性來說,個人覺得 + 是最可讀的,其次是 fmt.Sprintf,剩下的兩種方法可讀性差不多,都不怎麼可讀。

性能

很多時候我們都會考慮性能問題,但是,性能不是唯一的標準。這裡能,個人做了一些簡單的測試。

  • 使用 + 拼接字符串

代碼如下:

Go 語言中如何操作字符串才是最合適的

+

這裡再說一下,性能分析使用的 pprof,然後循環一千萬次,進行字符串的拼接,我們看一下效果:

Go 語言中如何操作字符串才是最合適的

+ pprof

這裡我們可以看出來,一千萬次循環,使用了 240ms。

  • 使用 strings.Join

代碼如下:

Go 語言中如何操作字符串才是最合適的

strings.Join

pprof 結果如下:

Go 語言中如何操作字符串才是最合適的

pprof

很顯然,同樣的循環次數,這回使用了 170ms

  • 使用 fmt.Sprintf

代碼如下:

Go 語言中如何操作字符串才是最合適的

fmt.Sprintf

pprof 結果如下:

Go 語言中如何操作字符串才是最合適的

pprof

同樣的循環次數,這回卻使用了 720ms 之久。

  • 使用 buffer

代碼如下:

Go 語言中如何操作字符串才是最合適的

buffer

pprof 結果:

Go 語言中如何操作字符串才是最合適的

pprof

這回使用的時間是 200ms。

現在我們可以總結一下:

在一千萬的循環下,string.Join(170ms) < buffer(200ms) < "+"(240ms) < fmt.Sprintf(720ms)。

那麼這就是唯一答案嗎?當然並不是,在循環次數不同的情況下,結果肯定是不同的,但是有一點是肯定的,如果遇到大量的數據拼接, buffer 效率是非常快的,strings.Join 也不差。而且還有一點也需要說一下,pprof 這種方式和使用 bench 測試也是不同的,在我印象中,使用 bench 的結果是, 大量數據 buffer 是最快的。

那麼我們在日常敲代碼時該如果使用呢?如果數據了小,或者只是使用一次,從性能上考慮,沒有什麼區別,但是 1ns 和 2ns 的區別還是有的,如果單個拼接,直接使用加號就好,方便,可讀,而且效率也是最高的。什麼?之前不是說 buffer 高嗎?是的,但是在單個拼接的情況下,加號效率是最高的,這一點需要使用 bench 來測試,這裡就不在給大家演示了。感興趣的可以動手試試。strings.Join 和 buffer 在單個字符串拼接時效率差不多,最慢的是 fmt.Sprintf。為什麼 fmt 這麼慢呢?其實主要原因還在於底層實現,一個字符串在底層就是一個字節切片,對於單個加號拼接來說,不過是兩個切片的合併,而之前的拉架變量就要靠 gc 了。但是 fmt.Sprintf 是會幫我們處理垃圾的。存在即合理嘛=..=

感謝大家閱讀!祝大家生活愉快。

"


分享到:


相關文章: