在總體的框架設計確定以後多注意一些編程細節積少成多可以獲得更佳的性能讓程序跑的更快!
前些天同事優化代碼時提到了String和StringBuffer仔細想想發現自己也就是知道個大概所以查了 一下資料文章中有錯誤的地方你可一定要告訴我啊呵呵^+^
一介紹
String:非可變類(immutable)一旦創建就不能再被改變
StringBuffer:可變類創建之後可以被改變
何謂非可變類?
簡單說非可變類的實例是不能被修改的每個實例中包含的信息都必須在該實例創建的時候就提供出來 並且在對象的整個生存周期內固定不變
非可變類好處狀態單一對象簡單便於維護通常是線程安全的用戶可以共享非可變對象甚至可 以共享它們的內部信息
二創建字符串
兩種方法
String s = hello;
String s = new String(hello)
哪種方式性能更好?
例:
//create String without new keyword
longstartTime=SystemcurrentTimeMillis()
for(inti=;i<;i++){
String str=
hello;
}
longendTime=SystemcurrentTimeMillis()
Systemoutprintln(create String without new keyword :
+(endTimestartTime)+
milli seconds)
//create String with new keyword
longstartTime=SystemcurrentTimeMillis()
for(inti=;i<;i++){
String str=
newString(hello)
}
longendTime=SystemcurrentTimeMillis()
Systemoutprintln(create String with new keyword :
+(endTimestartTime)+
milli seconds)
輸出結果為(注程序的輸出結果也許和你的結果不同但是總體趨勢應該是一致的)
create String withoutnewkeyword :milli seconds
create String withnewkeyword :milli seconds
結論創建字符串變量時盡可能不使用new關鍵字
說明
雖然兩個語句都是返回一個String對象的引用但是jvm對兩者的處理方式是不一樣的
對於第一種不用new關鍵字創建String對象
JVM首先會在內部維護的滯留字符串中通過String的equels方法查找是對象池中是否存放有該 String對象如果有返回已有的String對象給用戶而不會在heap中重新創建一個新的String對象如 果對象池中沒有該String對象JVM則在heap中創建新的String對象將其引用返回給用戶同時將該引 用添加至滯留字符串中
對於第二種使用new關鍵字創建String對象
JVM會馬上在heap中創建一個String對象然後將該對象的引用返回給用戶JVM是不會主動把該 對象放到滯留字符串裡面的除非程序調用 String的intern()方法
JVM為字符串自變量維護一些唯一的String對象程序員不需要為字符串自變量而發愁但是使用new關鍵 字可能會在內存中創建重復的String對象你不必為此而煩惱intern()方法可以幫你解決問題
Stringintern()檢查字符串對象的存在性如果需要的字符串對象已經存在那麼它會將引用指向已 經存在的字符串對象而不是重新創建一個
例:
String str=
world; /*JVM在滯留字符串中找不到值為world的字符串就在堆上創建一個string對象並將該對象的引用加入到滯留字符串中*/
String str=
newString(world) /*JVM在堆上重新創建一個值為world的字符串此時堆上有兩個值為world的字符串*/
if(str==str){
Systemoutprintln(str == str)
}
else{
Systemoutprintln(str != str)
}
//輸出str != str
String str=
world; /*JVM在發現滯留字符串中存在world對象因此返回str指向的對象給str即str和str是指向同一個對象的引用*/
if(str==str){
Systemoutprintln(str == str)
}
else{
Systemoutprintln(str != str)
}
//輸出str == str
str=strintern() /*此時JVM發現滯留字符串中已經存在world對象因此返回str指向的對象給str即str和str是指向同一個對象的引用*/
if(str==str){
Systemoutprintln(after intern() str == str)
}
else{
Systemoutprintln(after intern() str != str)
}
//輸出after intern() str == str
結論如果使用new關鍵字創建了字符串變量則盡可能使用intern()方法
上面的例子執行正是用到了string對象的不可變性質既然string對象一旦創建就不可以改變那麼多個 引用指向同一個對象就不會對彼此產生影響
三字符串連接
你可以使用+操作符或者ncat()或者StringBufferappend()等辦法來連接多個字符串哪 一種方法性能最佳?
如何選擇取決於兩種情景
第一種情景需要連接的字符串是在編譯期間決定的還是在運行期間決定
第二種情景你使用的是 StringBuffer還是String
通常程序員會認為StringBufferappend()方法會優於+操作符或 ncat()方法但是在一些特定 的情況下這個假想是不成立的
) 第一種情景編譯期間決定VS運行期間決定
//test the string Concatenation
longstartTime=SystemcurrentTimeMillis()
for(intk=; k<; k++){
String str=
this is
+
a test
+
for string concatenation;
}
longendTime=SystemcurrentTimeMillis()
Systemoutprintln(string concatenation using + :
+(endTimestartTime)+
milli seconds)
longstartTime=SystemcurrentTimeMillis()
for(intk=; k<; k++){
String str=
this is;
ncat(a test)
ncat(for string concatenation)
}
longendTime=SystemcurrentTimeMillis()
Systemoutprintln(string concatenation using concat() :
+(endTimestartTime)+
milli seconds)
longstartTime=SystemcurrentTimeMillis()
for(intl=; l<; l++){
StringBuffer sb=
newStringBuffer()
sbappend(this is)
sbappend(a test)
sbappend(for string concatenation)
}
longendTime=SystemcurrentTimeMillis()
Systemoutprintln(string concatenation using append() :
+(endTimestartTime)+
milli seconds)
上面代碼的輸出結果
string concatenation using
+
:
milli seconds
string concatenation using concat() :
milli seconds
string concatenation using append() :
milli seconds
很有趣
+操作符比StringBuffer
append()方法要快
Why?
這是因為編譯器對簡單的字符串進行了優化
即使使用new關鍵字來創建String對象的時候也是如此
例 如
編譯前
String str
=
this is
+
a test
+
for string concatenation
;
編譯後
String str
=
this is a test for string concatenation
;
這裡String對象在編譯期間就決定了而StringBuffer對象是在運行期間決定的
運行期間決定需要額外的 開銷
結論
如果字符串在編譯期間就可以決定它的值
則字符串拼接的時候
+
操作符效率更高
簡單的 認為append()效率高於
+
是錯誤的
) 第二種情景
StringBufferVSString
//string concatenation using
+=
longstartTime
=System
currentTimeMillis()
String str
=
hello
;
for(inti=
; i<
; i++){
str
+=
hello
;
}
longendTime
=System
currentTimeMillis()
System
out
println(
string concatenation using
+=
:
+(endTime
startTime
)+
milli seconds
)
//string concatenation using append()
longstartTime
=System
currentTimeMillis()
StringBuffer sb
=
newStringBuffer(
hello
)
for(inti=
; i<
; i++){
sb
append(
hello
)
}
longendTime
=System
currentTimeMillis()
System
out
println(
string concatenation using append() :
+(endTime
startTime
)+
milli seconds
)
上面代碼的輸出結果
string concatenation using
+=
:
milli seconds
string concatenation using append() :
milli seconds
結論
避免使用
+=
來構造字符串
雖然兩者都是在運行期間決定字符串對象
但是使用+=操作符會產生更多的臨時對象
在上例中
由於String類是不可變的
所以進行字符串拼接的時候
每循環一次就會產生臨時對象來保存 str
和
hello
的值
之後創建一個臨時的StringBuffer對象
並調用其append()方法來完成字符串的 拼接
最後調用toString()方法
將臨時StringBuffer對象轉為String再賦值給str
此時str
已經改變
指向了新的對象
) 第三種情景
設置StringBuffer的容量來提升性能
longstartTime
=System
currentTimeMillis()
StringBuffer sb
=
newStringBuffer(
hello
)
for(inti=
; i<
; i++){
sb
append(
hello
)
}
longendTime
=System
currentTimeMillis()
System
out
println(
string concatenation using append() :
+(endTime
startTime
)+
milli seconds
)
//set the StringBuffer capacity
longstartTime
=System
currentTimeMillis()
StringBuffer sb
=
newStringBuffer(
hello
)
sb
ensureCapacity(
)
for(inti=
; i<
; i++){
sb
append(
hello
)
}
longendTime
=System
currentTimeMillis()
System
out
println(
string concatenation using append() after set the StringBuffer capacity :
milli seconds
)
上面代碼的輸出結果
string concatenation using append() :
milli seconds
string concatenation using append() after set the StringBuffer capacity :
milli seconds
結論
聲明StringBuffer對象的時候
指定合適的capacity
會提升程序性能
)使用StringBuffer的構造函數來設定它的初始化容量
StringBuffer(int length)
)使用ensureCapacity(int minimumcapacity)方法在StringBuffer對象創建之後設置它的容量
首先我們看看StringBuffer的缺省行為
然後再找出一條更好的提升性能的途徑
StringBuffer的缺省行為
StringBuffer在內部維護一個字符數組
當你使用缺省的構造函數來創建StringBuffer對象的時候
StringBuffer的容量被初始化為
個字符
也就是說缺省容量就是
個字符
當StringBuffer達到最大容 量的時候
它會將自身容量增加到當前的
倍再加
也就是(
*舊值+
)
如果你使用缺省值
初始化之後接著往裡面追加字符
在你追加到第
(原文是
其實是錯誤的
因為在追加到第
個字符的時候
容量不會發生變化
很抱歉
以後會更嚴謹一些^+^)個字符的時候它會將容量增加 到
(
*
+
)
當追加到
個字符的時候就會將容量增加到
(
*
+
)
無論何事只要StringBuffer 到達它的最大容量它就不得不創建一個新的字符數組然後重新將舊字符和新字符都拷貝一遍
所以給 StringBuffer設置一個合理的初始化容量值
會提升程序的性能
但是為什麼容量變化的時候是
*舊值+
呢?有誰能告訴我麼?查資料的時候沒有找到
附
查資料的過程中發現了jdk
還提供了StringBuilder類
我是沒有用過
不過也介紹一下好了
Java
lang
StringBuffer 線程安全的可變字符序列
類似於 String 的字符串緩沖區
但不能修 改
可將字符串緩沖區安全地用於多個線程
可以在必要時對這些方法進行同步
因此任意特定實例上的 所有操作就好像是以串行順序發生的
該順序與所涉及的每個線程進行的方法調用順序一致
每個字符串緩沖區都有一定的容量
只要字符串緩沖區所包含的字符序列的長度沒有超出此容量
就無需分配新的內部緩沖區數組
如果內部緩沖區溢出
則此容量自動增大
從 JDK
開始
為該 類增添了一個單個線程使用的等價類
即 StringBuilder
與該類相比
通常應該優先使用 StringBuilder 類
因為它支持所有相同的操作
但由於它不執行同步
所以速度更快
但是如果將 StringBuilder 的實例用於多個線程是不安全的
需要這樣的同步
則建議使用 StringBuffer
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26749.html