国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
string 和stringbuffer的速度比較

 

public class Main{ public static void main(String[] args){ /* 1 */ String string = 'a' 'b' 'c'; /* 2 */ StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append('a'); stringBuffer.append('b'); stringBuffer.append('c'); string = stringBuffer.toString(); } }

  當(dāng)時大部分的新手猿友都表示,stringbuffer快于string 。唯有群里一位有工作經(jīng)驗(yàn)的猿友說,是string 的速度快。這讓LZ意識到,工作經(jīng)驗(yàn)確實(shí)不是白積累的,一個小問題就看出來了。

  這里確實(shí)string 的寫法要比stringbuffer快,是因?yàn)樵诰幾g這段程序的時候,編譯器會進(jìn)行常量優(yōu)化,它會將a、b、c直接合成一個常量abc保存在對應(yīng)的class文件當(dāng)中。LZ當(dāng)時在群里貼出了編譯后的class文件的反編譯代碼,如下。

public class Main{ public static void main(String[] args) { String string = 'abc'; StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append('a'); stringBuffer.append('b'); stringBuffer.append('c'); string = stringBuffer.toString(); }}

  可以看出,在編譯這個java文件時,編譯器已經(jīng)直接進(jìn)行了 運(yùn)算,這是因?yàn)閍、b、c這三個字符串都是常量,是可以在編譯期由編譯器完成這個運(yùn)算的。假設(shè)我們換一種寫法。

public class Main{ public static void main(String[] args){ /* 1 */ String a = 'a'; String b = 'b'; String c = 'c'; String string = a b c; /* 2 */ StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(a); stringBuffer.append(b); stringBuffer.append(c); string = stringBuffer.toString(); } }

  此處的答案貌似應(yīng)該是stringbuffer更快,因?yàn)榇藭ra、b、c都是對象,編譯器已經(jīng)無法在編譯期進(jìn)行提前的運(yùn)算優(yōu)化了。

  但是,事實(shí)真的是這樣的嗎?

  其實(shí)答案依然是第一種寫法更快,也就是string 的寫法更快,這一點(diǎn)可能會有猿友比較疑惑。這個原因是因?yàn)閟tring 其實(shí)是由stringbuilder完成的,而一般情況下stringbuilder要快于stringbuffer,這是因?yàn)閟tringbuilder線程不安全,少了很多線程鎖的時間開銷,因此這里依然是string 的寫法速度更快。

  盡管LZ已經(jīng)解釋了原因,不過可能還是有猿友依然不太相信,那么下面我們來寫一個測試程序。

public class Main{ public static void main(String[] args) { String a = 'a'; String b = 'b'; String c = 'c'; long start = System.currentTimeMillis(); for (int i = 0; i < 100000000; i ) { String string = a b c; if (string.equals('abc')) {} } System.out.println('string cost time:' (System.currentTimeMillis() - start) 'ms'); start = System.currentTimeMillis(); for (int i = 0; i < 100000000; i ) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(a); stringBuffer.append(b); stringBuffer.append(c); String string = stringBuffer.toString(); if (string.equals('abc')) {} } System.out.println('stringbuffer cost time:' (System.currentTimeMillis() - start) 'ms'); }}

  我們每個進(jìn)行了1億次,我們會看到string 竟然真的快于stringbuffer,是不是瞬間被毀了三觀,我們來看下結(jié)果。

  答案已經(jīng)很顯然,string 竟然真的比stringbuffer要快。這里其實(shí)還是編譯器搗的鬼,string 事實(shí)上是由stringbuilder完成的。我們來看一下這個程序的class文件內(nèi)容就可以看出來了。

  由于文件太長,所以LZ是分開截的圖??梢钥吹?,里面有兩次stringbuilder的append方法調(diào)用,三次stringbuffer的append方法調(diào)用。stringbuilder只有兩次append方法的調(diào)用,是因?yàn)樵趧?chuàng)建stringbuilder對象的時候,第一個字符串也就是a對象已經(jīng)被當(dāng)做構(gòu)造函數(shù)的參數(shù)傳入了進(jìn)去,因此就少了一次append方法。

  不過請各位猿友不要誤會,這里stringbuilder之所以比stringbuffer快,是因?yàn)樯倭随i同步的開銷,而不是因?yàn)樯倭艘淮蝍ppend方法,原因看下面這段stringbuilder類的源碼就知道了。

public StringBuilder(String str) { super(str.length() 16); append(str); }

  可以看到,實(shí)際上帶有string參數(shù)的構(gòu)造方法,依然是使用的append方法,因此stringbuilder其實(shí)也進(jìn)行了三次append方法的調(diào)用。

  看到這里,估計(jì)有的猿友就該奇怪了,這么看的話,似乎string 的速度比stringbuffer更快,難道以前的認(rèn)識都錯誤了?

  答案當(dāng)然是否定的,我們來看下面這個小程序,你就看出來差別有多大了。

public class Main{ public static void main(String[] args) { String a = 'a'; long start = System.currentTimeMillis(); String string = a; for (int i = 0; i < 100000; i ) { string = a; } if (string.equals('abc')) {} System.out.println('string cost time:' (System.currentTimeMillis() - start) 'ms'); start = System.currentTimeMillis(); StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < 100000; i ) { stringBuffer.append(a); } if (stringBuffer.toString().equals('abc')) {} System.out.println('stringbuffer cost time:' (System.currentTimeMillis() - start) 'ms'); }}

  這個程序與剛才的程序有著細(xì)微的差別,但是結(jié)果卻會讓你大跌眼鏡。我們來看結(jié)果輸出。

  看到這個結(jié)果是不是直接給跪了,效率差了這么多?這還是LZ將循環(huán)次數(shù)降到了10萬,而不是1億,因?yàn)?億次LZ跑了很久也沒跑完,LZ等不急了,0.0。

  造成這種情況的原因,我們看兩個程序的區(qū)別就看出來了。第一個循環(huán)1億次的程序,不管是string 還是stringbuffer都是在循環(huán)體里構(gòu)造的字符串,最重要的是string 是由一個語句構(gòu)造而成的,因此此時string 其實(shí)和stringbuffer實(shí)際運(yùn)行的方式是一樣的,只不過string 是使用的stringbuilder而已。

  而對于上面這個10萬次循環(huán)的程序,stringbuffer就不用說了,實(shí)際運(yùn)行的方式很明顯。而對于string ,它將會創(chuàng)造10萬個stringbuilder對象,每一次循環(huán)體的發(fā)生,都相當(dāng)于我們新建了一個stringbuilder對象,將string對象作為構(gòu)造函數(shù)的參數(shù),并進(jìn)行一次append方法和一次toString方法。

  由上面幾個小程序我們可以看出,在string 寫成一個表達(dá)式的時候(更準(zhǔn)確的說,是寫成一個賦值語句的時候),效率其實(shí)比stringbuffer更快,但如果不是這樣的話,則效率會明顯低于stringbuffer。我們來再寫一個程序證實(shí)這一點(diǎn)。

  為了不會導(dǎo)致編譯失敗,我們將循環(huán)次數(shù)減為1萬次,否則會超出文件的最大長度,我們先來看看剛才的程序改為1萬次循環(huán)的結(jié)果。

  可以看到,在1萬次的循環(huán)下,依然可以看到效率上的明顯差異,這個差距已經(jīng)足夠我們觀察了。現(xiàn)在我們就改一種寫法,它會讓string 的效率提高到stringbuffer的速度,甚至更快。

  這里我們是將1萬次字符串的拼接直接寫成了一個表達(dá)式,那個a a ...表達(dá)式一共是1萬個(是LZ使用循環(huán)打印出來貼到代碼處的),可以看到,此時string 的速度已經(jīng)超過了stringbuffer。

  因此LZ給各位猿友一個建議,如果是有限個string 的操作,可以直接寫成一個表達(dá)式的情況下,那么速度其實(shí)與stringbuffer是一樣的,甚至更快,因此有時候沒必要就幾個字符串操作也要建個stringbuffer(如果中途拼接操作的字符串是線程間共享的,那么也建議使用stringbuffer,因?yàn)樗蔷€程安全的)。但是如果把string 的操作拆分成語句去進(jìn)行的話,那么速度將會指數(shù)倍下降。

  總之,我們大部分時候的宗旨是,如果是string 操作,我們應(yīng)該盡量在一個語句中完成。如果是無法做到,并且拼接動作很多,比如數(shù)百上千成萬次,則必須使用stringbuffer,不能用string ,否則速度會很慢。

 

Java的方法參數(shù)傳遞方式

 

  這個問題的引入是當(dāng)時LZ在群里問了這樣一個問題,就是Java的方法參數(shù)傳遞是值傳遞還是引用傳遞?對于基本類型和對象來說,都會發(fā)生什么情況?

  這道題大部分猿友還是說的不錯的,包括群里的新手猿友。答案是Java只有值傳遞,因?yàn)镴ava只有值傳遞,因此在改變形參的值的時候,實(shí)參是不會因此而改變的。這一點(diǎn)從下面這個小程序可以很明顯的看出來。

public class Main{ public static void main(String[] args) { int a = 2; Object object = new Object(); System.out.println(a ':' object); change(a, object); System.out.println(a ':' object); } public static void change(int a,Object object){ a = 1; object = new Object(); }}

  我們在方法當(dāng)中改變形參的值,之后再次輸出兩個實(shí)參的值,會發(fā)現(xiàn)它們無任何變化。

  這就足以說明Java只有值傳遞了,無論是對象還是基本類型,改變形參的值不會反應(yīng)到實(shí)參上面去,這也正是值傳遞的奧義所在。

  對于基本類型來說,這一點(diǎn)比較明顯,不過對于對象來講,很多猿友會有誤解。認(rèn)為我們在方法里改變形參對象屬性的值,是會反映到實(shí)參上面去的,因此部分猿友認(rèn)為這就是引用傳遞。

  首先LZ要強(qiáng)調(diào)的是,上面也說了,我們只是改變形參對象屬性的值,反映到實(shí)參上面去的,而不是真的改變了實(shí)參的值,也就是說實(shí)參引用的對象依然是原來的對象,只不過對象里的屬性值改變了而已。

  針對上面這一點(diǎn),我們使用下面這個程序來說明。

public class Main{ public static void main(String[] args) { int a = 2; Entity entity = new Entity(); entity.a = 100; System.out.println(a ':' entity); System.out.println(entity.a); change(a, entity); System.out.println(a ':' entity); System.out.println(entity.a); } public static void change(int a,Entity entity){ a = 1; entity.a = 200; }}class Entity{ int a;}

  我們在方法里改變了entity對象的屬性值為200,我們來看一下結(jié)果。

  可以看到,實(shí)參對象的值依然沒有改變,只是屬性值變了而已,因此這依舊是值傳遞的范圍。為了說明這個區(qū)別,我們來看下真正的引用傳遞。由于Java當(dāng)中不存在引用傳遞,因此LZ借用C/C 來讓各位看下真正的引用傳遞是什么效果。

1 #include <stdio.h> 2 3 class Entity{ 4 public: 5 int a; 6 Entity(){}; 7 }; 8 9 void change(int &a,Entity *&entity);10 11 int main(){12 int a = 2;13 Entity *entity = new Entity();14 printf('%d:%p\n',a,entity);15 change(a, entity);16 printf('%d:%p\n',a,entity);17 }18 19 void change(int &a,Entity *&entity){20 a = 1;21 entity = new Entity();22 }

  LZ盡量保持和Java的第一個程序是一樣的結(jié)構(gòu),只不過C/C 中沒有現(xiàn)成的Object對象,因此這里使用Entity對象代替,這樣便于各位猿友理解。我們來看下結(jié)果,結(jié)果會發(fā)現(xiàn)引用傳遞的時候,在方法里改變形參的值會直接反應(yīng)到實(shí)參上面去。

  可以看到,在引用傳遞的時候,無論是基本類型,還是對象類型,實(shí)參的值都發(fā)生了變化,這里才是真正的引用傳遞。當(dāng)然了,LZ對C/C 的理解非常有限,不過毋庸置疑的是,真正的引用傳遞應(yīng)該是類似上述的現(xiàn)象,也就是說實(shí)參會因形參的改變而改變的現(xiàn)象,而這顯然不是我們Java程序當(dāng)中的現(xiàn)象。

  因此,結(jié)論就是Java當(dāng)中只有值傳遞,但是這并不影響我們在方法中改變對象參數(shù)的屬性值。

  

文章小結(jié)

 

  我們平時多了解一些語言的特性確實(shí)是有很多好處的,這會潛移默化的影響我們編碼的質(zhì)量。希望各位猿友在遇到這種問題的時候也自己多寫寫代碼,看看自己的理解對不對,在這樣的過程中進(jìn)步會很快,尤其是在初次接觸一個編程語言的時候。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
String,StringBuffer,StringBuild的區(qū)別-程序開發(fā)-紅黑聯(lián)盟
Java認(rèn)證考試:Java String常用方法詳解
java中String類、StringBuilder類和StringBuffer類詳解
StringBuffer和StringBuilder的區(qū)別
Java中String和StringBuilder
String、StringBuffer和StringBuilder類的區(qū)別
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服