C++字符串拼接效率對(duì)比(+=、append、stringstream、sprintf)
C++字符串拼接效率對(duì)比
事情起因很簡(jiǎn)單,自己的代碼中使用了stringstream對(duì)象進(jìn)行字符串的拼接,然后被老同事質(zhì)疑效率低下。借著這個(gè)機(jī)會(huì)了解下為什么?
一、+=、append、stringsteam、sprintf四種字符串拼接方法比較
C/C++中字符串拼接的使用場(chǎng)景非常多,字符串拼接的方法也非常多,這里簡(jiǎn)單的比對(duì)下上述四種方法的效率。
測(cè)試方法:
分別采用+=、append、stringstream、sprintf的方式來拼接字符串。
s1=“aaaaa”,s2=“bbbbb”,s3=“ccccc”。
內(nèi)層循環(huán)將這三個(gè)字符串拼接100次;此外還有一個(gè)外層循環(huán),循環(huán)次數(shù)自己定義(此處設(shè)為100000)。
程序如下:
#include <iostream> #include <string> #include <sys/time.h> #include <sstream> #include <stdio.h> using namespace std; #define OUT_IN_REPEATE_NUM 100000 #define IN_REPEATE_NUM 100 //內(nèi)層循環(huán)將s1、s2、s3循環(huán)拼接100次 string s1="aaaaaa"; string s2="bbbbbb"; string s3="cccccc"; void plusTest(string& ret) { for(int i=0; i<IN_REPEATE_NUM; i++) { ret += s1; ret += s2; ret += s3; } } void appendTest(string& ret) { for(int i=0; i<IN_REPEATE_NUM; i++) { ret.append(s1); ret.append(s2); ret.append(s3); } } void sprintfTest(string& ret) { const size_t length=26*IN_REPEATE_NUM; char tmp[length]; char* cp = tmp; size_t strLength=s1.length()+s2.length()+s3.length(); for(int i=0; i<IN_REPEATE_NUM; i++) { sprintf(cp,"%s%s%s", s1.c_str(), s2.c_str(),s3.c_str()); cp+=strLength; } ret = tmp; } void ssTest(string& ret) { stringstream ss; for(int i=0; i<IN_REPEATE_NUM; i++) { ss<<s1; ss<<s2; ss<<s3; } ret = ss.str(); } int main() { string ss, plus, append, sprintf; struct timeval sTime, eTime; gettimeofday(&sTime, NULL); for(int i=0; i<OUT_IN_REPEATE_NUM; i++) { sprintf=""; sprintfTest(sprintf); } gettimeofday(&eTime, NULL); long SprintfTime = (eTime.tv_sec-sTime.tv_sec)*1000000+(eTime.tv_usec-sTime.tv_usec); //exeTime 單位是微秒 gettimeofday(&sTime, NULL); for(int i=0; i<OUT_IN_REPEATE_NUM; i++) { append=""; appendTest(append); } gettimeofday(&eTime, NULL); long AppendTime = (eTime.tv_sec-sTime.tv_sec)*1000000+(eTime.tv_usec-sTime.tv_usec); //exeTime 單位是微秒 gettimeofday(&sTime, NULL); for(int i=0; i<OUT_IN_REPEATE_NUM; i++) { ss=""; ssTest(ss); } gettimeofday(&eTime, NULL); long SsTime = (eTime.tv_sec-sTime.tv_sec)*1000000+(eTime.tv_usec-sTime.tv_usec); //exeTime 單位是微秒 gettimeofday(&sTime, NULL); for(int i=0; i<OUT_IN_REPEATE_NUM; i++) { plus=""; plusTest(plus); } gettimeofday(&eTime, NULL); long PlusTime = (eTime.tv_sec-sTime.tv_sec)*1000000+(eTime.tv_usec-sTime.tv_usec); //exeTime 單位是微秒 cout<<"PlusTime is : "<<PlusTime<<endl; cout<<"AppendTime is : "<<AppendTime<<endl; cout<<"SsTime is : "<<SsTime<<endl; cout<<"SprintfTime is :"<<SprintfTime<<endl; if(ss==sprintf && append==plus && ss==plus) { cout<<"result string are same!"<<endl; } else { cout<<"Different!"<<endl; cout<<"Sprintf: "<<sprintf<<endl; cout<<"ss: "<<ss<<endl; cout<<"Plus: "<<plus<<endl; cout<<"Append:"<<append<<endl; } }
結(jié)果如下:
可以看到+=、append、stringstream、sprintf四種方式在消耗的時(shí)間大致為1:1:4:2。
好吧,stringstream確實(shí)好慢,人家說的是對(duì)的。
二、關(guān)于stringstream
stringstream優(yōu)點(diǎn):可以方便的以流運(yùn)算符<<將數(shù)值以各種數(shù)據(jù)(字串、數(shù)值)寫入stringstream對(duì)象,且不用擔(dān)心寫越界等問題;其中類型安全不會(huì)溢出的特性非常搶眼。
stringstream缺點(diǎn):相對(duì)于其他方法效率較低。一方面寫入時(shí)的動(dòng)態(tài)內(nèi)存分配需要一定的開銷,另一方面其成員函數(shù)str()在去除字符串的時(shí)候會(huì)進(jìn)行一次字符串的值拷貝也影響效率。
stringstream對(duì)象的構(gòu)造和析構(gòu)函數(shù)通常是非常消耗時(shí)間,畢竟涉及到內(nèi)存的分配、對(duì)象的構(gòu)造。
上述測(cè)試結(jié)果也顯示其效率明顯低于”+=”、“append“。
當(dāng)然這個(gè)時(shí)間消耗也是和stringstream對(duì)象被創(chuàng)建了多少次密切相關(guān)的。
也就是說如果能在多次轉(zhuǎn)換(for循環(huán))中重復(fù)使用同一個(gè)stringstream(而不是每次都創(chuàng)建一個(gè)新的對(duì)象)就還好。
但是記得每次循環(huán)使用前使用clear()、str("")方法(如下)。
void* test_stringstream(void * arg) { stringstream oss; for(int i=0;i<10000;i++) { oss.clear();這僅僅置流標(biāo)記 oss.str("");/這是才是真正清空操作 oss << i; } }
字符串拼接執(zhí)行速度和內(nèi)存消耗比較
public static void main(String[] args) { long start = 0L; long end = 0L; System.out.println("字符串拼接執(zhí)行效率比較:"); String s1 = ""; start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) {//十萬次 s1 = s1 + "a"; } end = System.currentTimeMillis(); System.out.println("1、+ 方式拼接10萬次耗時(shí):" + (end - start) + "毫秒!"); String s2 = ""; start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) {//十萬次 s2 += "b"; } end = System.currentTimeMillis(); System.out.println("2、+= 方式拼接10萬次耗時(shí):" + (end - start) + "毫秒!"); StringBuffer bf = new StringBuffer(); start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) {//千萬次 bf.append("c"); } end = System.currentTimeMillis(); System.out.println("3、StringBuffer.append 方式拼接1000萬次耗時(shí):" + (end - start) + "毫秒!"); StringBuilder bl=new StringBuilder(); start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) {//千萬次 bl.append("d"); } end = System.currentTimeMillis(); System.out.println("4、StringBuilder.append 方式拼接1000萬次耗時(shí):" + (end - start) + "毫秒!"); }
輸出結(jié)果:
字符串拼接執(zhí)行效率比較
1、+ 方式拼接10萬次耗時(shí):4561毫秒!
2、+= 方式拼接10萬次耗時(shí):4491毫秒!
3、StringBuffer.append 方式拼接1000萬次耗時(shí):189毫秒!
4、StringBuilder.append 方式拼接1000萬次耗時(shí):141毫秒!
解釋:+ 方式本質(zhì)是 s = new StringBuilder(s).append("a") .toString();
耗時(shí)間的地方不是 append,而是 toString,執(zhí)行一次 toString 耗時(shí)在幾微秒到幾毫秒不等
內(nèi)存消耗:
+ > += > StringBuffer = StringBuilder
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)文件的生成和使用教程
庫(kù)文件是計(jì)算機(jī)上的一類文件,可以簡(jiǎn)單的把庫(kù)文件看成一種代碼倉(cāng)庫(kù),它提供給使用者一些可以直接拿來用的變量、函數(shù)和類,下面這篇文章主要給大家介紹了關(guān)于C++靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)文件的生成和使用的相關(guān)資料,需要的朋友可以參考下2023-03-03C語言實(shí)現(xiàn)考試報(bào)名管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)考試報(bào)名管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06C語言字符函數(shù)中的isalnum()和iscntrl()你都知道嗎
這篇文章主要為大家詳細(xì)介紹了C語言字符函數(shù)中的isalnum()和iscntrl(),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++實(shí)現(xiàn)與Lua相互調(diào)用的示例詳解
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)與Lua相互調(diào)用的方法,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-03-03