您好,登錄后才能下訂單哦!
本篇內容主要講解“Java字符串操作、基本運算方法等優化方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java字符串操作、基本運算方法等優化方法是什么”吧!
字符串操作優化
字符串對象
字符串對象或者其等價對象 (如 char 數組),在內存中總是占據***的空間塊,因此如何高效地處理字符串,是提高系統整體性能的關鍵。
String 對象可以認為是 char 數組的延伸和進一步封裝,它主要由 3 部分組成:char 數組、偏移量和 String 的長度。char 數組表示 String 的內容,它是 String 對象所表示字符串的超集。String 的真實內容還需要由偏移量和長度在這個 char 數組中進行定位和截取。
String 有 3 個基本特點:
1. 不變性;
2. 針對常量池的優化;
3. 類的 final 定義。
不變性指的是 String 對象一旦生成,則不能再對它進行改變。String 的這個特性可以泛化成不變 (immutable) 模式,即一個對象的狀態在對象被創建之后就不再發生變化。不變模式的主要作用在于當一個對象需要被多線程共享,并且訪問頻繁時,可以省略同步和鎖等待的時 間,從而大幅提高系統性能。
針對常量池的優化指的是當兩個 String 對象擁有相同的值時,它們只引用常量池中的同一個拷貝,當同一個字符串反復出現時,這個技術可以大幅度節省內存空間。
下面代碼 str1、str2、str4 引用了相同的地址,但是 str3 卻重新開辟了一塊內存空間,雖然 str3 單獨占用了堆空間,但是它所指向的實體和 str1 完全一樣。代碼如下清單 1 所示。
清單 1. 示例代碼
public class StringDemo { public static void main(String[] args){ String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = str1; System.out.println("is str1 = str2?"+(str1==str2)); System.out.println("is str1 = str3?"+(str1==str3)); System.out.println("is str1 refer to str3?"+(str1.intern()==str3.intern())); System.out.println("is str1 = str4"+(str1==str4)); System.out.println("is str2 = str4"+(str2==str4)); System.out.println("is str4 refer to str3?"+(str4.intern()==str3.intern())); } }
輸出如清單 2 所示。
清單 2. 輸出結果
is str1 = str2?true is str1 = str3?false is str1 refer to str3?true is str1 = str4true is str2 = str4true is str4 refer to str3?true
SubString 使用技巧
String 的 substring 方法源碼在***一行新建了一個 String 對象,new String(offset+beginIndex,endIndex-beginIndex,value);該行代碼的目的是為了能高效且快速地共享 String 內的 char 數組對象。但在這種通過偏移量來截取字符串的方法中,String 的原生內容 value 數組被復制到新的子字符串中。設想,如果原始字符串很大,截取的字符長度卻很短,那么截取的子字符串中包含了原生字符串的所有內容,并占據了相應的內存空 間,而僅僅通過偏移量和長度來決定自己的實際取值。這種算法提高了速度卻浪費了空間。
下面代碼演示了使用 substring 方法在一個很大的 string 獨享里面截取一段很小的字符串,如果采用 string 的 substring 方法會造成內存溢出,如果采用反復創建新的 string 方法可以確保正常運行。
清單 3.substring 方法演示
import java.util.ArrayList; import java.util.List; public class StringDemo { public static void main(String[] args){ List<String> handler = new ArrayList<String>(); for(int i=0;i<1000;i++){ HugeStr h = new HugeStr(); ImprovedHugeStr h2 = new ImprovedHugeStr(); handler.add(h.getSubString(1, 5)); handler.add(h2.getSubString(1, 5)); } } static class HugeStr{ private String str = new String(new char[800000]); public String getSubString(int begin,int end){ return str.substring(begin, end); } } static class ImprovedHugeStr{ private String str = new String(new char[10000000]); public String getSubString(int begin,int end){ return new String(str.substring(begin, end)); } } }
輸出結果如清單 4 所示。
清單 4. 輸出結果
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Unknown Source) at java.lang.StringValue.from(Unknown Source) at java.lang.String.<init>(Unknown Source) at StringDemo$ImprovedHugeStr.<init>(StringDemo.java:23) at StringDemo.main(StringDemo.java:9)
ImprovedHugeStr 可以工作是因為它使用沒有內存泄漏的 String 構造函數重新生成了 String 對象,使得由 substring() 方法返回的、存在內存泄漏問題的 String 對象失去所有的強引用,從而被垃圾回收器識別為垃圾對象進行回收,保證了系統內存的穩定。
String 的 split 方法支持傳入正則表達式幫助處理字符串,但是簡單的字符串分割時性能較差。
對比 split 方法和 StringTokenizer 類的處理字符串性能,代碼如清單 5 所示。
切分字符串方式討論
String 的 split 方法支持傳入正則表達式幫助處理字符串,操作較為簡單,但是缺點是它所依賴的算法在對簡單的字符串分割時性能較差。清單 5 所示代碼對比了 String 的 split 方法和調用 StringTokenizer 類來處理字符串時性能的差距。
清單 5.String 的 split 方法演示
import java.util.StringTokenizer; public class splitandstringtokenizer { public static void main(String[] args){ String orgStr = null; StringBuffer sb = new StringBuffer(); for(int i=0;i<100000;i++){ sb.append(i); sb.append(","); } orgStr = sb.toString(); long start = System.currentTimeMillis(); for(int i=0;i<100000;i++){ orgStr.split(","); } long end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); String orgStr1 = sb.toString(); StringTokenizer st = new StringTokenizer(orgStr1,","); for(int i=0;i<100000;i++){ st.nextToken(); } st = new StringTokenizer(orgStr1,","); end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); String orgStr2 = sb.toString(); String temp = orgStr2; while(true){ String splitStr = null; int j=temp.indexOf(","); if(j<0)break; splitStr=temp.substring(0, j); temp = temp.substring(j+1); } temp=orgStr2; end = System.currentTimeMillis(); System.out.println(end-start); } }
輸出如清單 6 所示:
清單 6. 運行輸出結果
39015
16
15
當一個 StringTokenizer 對象生成后,通過它的 nextToken() 方法便可以得到下一個分割的字符串,通過 hasMoreToken 方法可以知道是否有更多的字符串需要處理。對比發現 split 的耗時非常的長,采用 StringTokenizer 對象處理速度很快。我們嘗試自己實現字符串分割算法,使用 substring 方法和 indexOf 方法組合而成的字符串分割算法可以幫助很快切分字符串并替換內容。
由于 String 是不可變對象,因此,在需要對字符串進行修改操作時 (如字符串連接、替換),String 對象會生成新的對象,所以其性能相對較差。但是 JVM 會對代碼進行徹底的優化,將多個連接操作的字符串在編譯時合成一個單獨的長字符串。
以上實例運行結果差異較大的原因是 split 算法對每一個字符進行了對比,這樣當字符串較大時,需要把整個字符串讀入內存,逐一查找,找到符合條件的字符,這樣做較為耗時。而 StringTokenizer 類允許一個應用程序進入一個令牌(tokens),StringTokenizer 類的對象在內部已經標識化的字符串中維持了當前位置。一些操作使得在現有位置上的字符串提前得到處理。 一個令牌的值是由獲得其曾經創建 StringTokenizer 類對象的字串所返回的。
清單 7.split 類源代碼
import java.util.ArrayList; public class Split { public String[] split(CharSequence input, int limit) { int index = 0; boolean matchLimited = limit > 0; ArrayList<String> matchList = new ArrayList<String>(); Matcher m = matcher(input); // Add segments before each match found while(m.find()) { if (!matchLimited || matchList.size() < limit - 1) { String match = input.subSequence(index, m.start()).toString(); matchList.add(match); index = m.end(); } else if (matchList.size() == limit - 1) { // last one String match = input.subSequence(index,input.length()).toString(); matchList.add(match); index = m.end(); } } // If no match was found, return this if (index == 0){ return new String[] {input.toString()}; } // Add remaining segment if (!matchLimited || matchList.size() < limit){ matchList.add(input.subSequence(index, input.length()).toString()); } // Construct result int resultSize = matchList.size(); if (limit == 0){ while (resultSize > 0 && matchList.get(resultSize-1).equals("")) resultSize--; String[] result = new String[resultSize]; return matchList.subList(0, resultSize).toArray(result); } } }
split 借助于數據對象及字符查找算法完成了數據分割,適用于數據量較少場景。
合并字符串
由于 String 是不可變對象,因此,在需要對字符串進行修改操作時 (如字符串連接、替換),String 對象會生成新的對象,所以其性能相對較差。但是 JVM 會對代碼進行徹底的優化,將多個連接操作的字符串在編譯時合成一個單獨的長字符串。針對超大的 String 對象,我們采用 String 對象連接、使用 concat 方法連接、使用 StringBuilder 類等多種方式,代碼如清單 8 所示。
清單 8. 處理超大 String 對象的示例代碼
public class StringConcat { public static void main(String[] args){ String str = null; String result = ""; long start = System.currentTimeMillis(); for(int i=0;i<10000;i++){ str = str + i; } long end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); for(int i=0;i<10000;i++){ result = result.concat(String.valueOf(i)); } end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for(int i=0;i<10000;i++){ sb.append(i); } end = System.currentTimeMillis(); System.out.println(end-start); } }
輸出如清單 9 所示。
清單 9. 運行輸出結果
375
187
0
雖然***種方法編譯器判斷 String 的加法運行成 StringBuilder 實現,但是編譯器沒有做出足夠聰明的判斷,每次循環都生成了新的 StringBuilder 實例從而大大降低了系統性能。
StringBuffer 和 StringBuilder 都實現了 AbstractStringBuilder 抽象類,擁有幾乎相同的對外借口,兩者的***不同在于 StringBuffer 對幾乎所有的方法都做了同步,而 StringBuilder 并沒有任何同步。由于方法同步需要消耗一定的系統資源,因此,StringBuilder 的效率也好于 StringBuffer。 但是,在多線程系統中,StringBuilder 無法保證線程安全,不能使用。代碼如清單 10 所示。
清單 10.StringBuilderVSStringBuffer
public class StringBufferandBuilder { public StringBuffer contents = new StringBuffer(); public StringBuilder sbu = new StringBuilder(); public void log(String message){ for(int i=0;i<10;i++){ /* contents.append(i); contents.append(message); contents.append("/n"); */ contents.append(i); contents.append("/n"); sbu.append(i); sbu.append("/n"); } } public void getcontents(){ //System.out.println(contents); System.out.println("start print StringBuffer"); System.out.println(contents); System.out.println("end print StringBuffer"); } public void getcontents1(){ //System.out.println(contents); System.out.println("start print StringBuilder"); System.out.println(sbu); System.out.println("end print StringBuilder"); } public static void main(String[] args) throws InterruptedException { StringBufferandBuilder ss = new StringBufferandBuilder(); runthread t1 = new runthread(ss,"love"); runthread t2 = new runthread(ss,"apple"); runthread t3 = new runthread(ss,"egg"); t1.start(); t2.start(); t3.start(); t1.join(); t2.join(); t3.join(); } } class runthread extends Thread{ String message; StringBufferandBuilder buffer; public runthread(StringBufferandBuilder buffer,String message){ this.buffer = buffer; this.message = message; } public void run(){ while(true){ buffer.log(message); //buffer.getcontents(); buffer.getcontents1(); try { sleep(5000000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
輸出結果如清單 11 所示。
清單 11. 運行結果
start print StringBuffer 0123456789 end print StringBuffer start print StringBuffer start print StringBuilder 01234567890123456789 end print StringBuffer start print StringBuilder 01234567890123456789 01234567890123456789 end print StringBuilder end print StringBuilder start print StringBuffer 012345678901234567890123456789 end print StringBuffer start print StringBuilder 012345678901234567890123456789 end print StringBuilder
StringBuilder 數據并沒有按照預想的方式進行操作。StringBuilder 和 StringBuffer 的擴充策略是將原有的容量大小翻倍,以新的容量申請內存空間,建立新的 char 數組,然后將原數組中的內容復制到這個新的數組中。因此,對于大對象的擴容會涉及大量的內存復制操作。如果能夠預先評估大小,會提高性能。
數據定義、運算邏輯優化
使用局部變量
調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧 (Stack) 里面,讀寫速度較快。其他變量,如靜態變量、實例變量等,都在堆 (heap) 中創建,讀寫速度較慢。清單 12 所示代碼演示了使用局部變量和靜態變量的操作時間對比。
清單 12. 局部變量 VS 靜態變量
public class variableCompare { public static int b = 0; public static void main(String[] args){ int a = 0; long starttime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ a++;//在函數體內定義局部變量 } System.out.println(System.currentTimeMillis() - starttime); starttime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ b++;//在函數體內定義局部變量 } System.out.println(System.currentTimeMillis() - starttime); } }
運行后輸出如清單 13 所示。
清單 13. 運行結果
0
15
以上兩段代碼的運行時間分別為 0ms 和 15ms。由此可見,局部變量的訪問速度遠遠高于類的成員變量。
位運算代替乘除法
位運算是所有的運算中最為高效的。因此,可以嘗試使用位運算代替部分算數運算,來提高系統的運行速度。最典型的就是對于整數的乘除運算優化。清單 14 所示代碼是一段使用算數運算的實現。
清單 14. 算數運算
public class yunsuan { public static void main(String args[]){ long start = System.currentTimeMillis(); long a=1000; for(int i=0;i<10000000;i++){ a*=2; a/=2; } System.out.println(a); System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); for(int i=0;i<10000000;i++){ a<<=1; a>>=1; } System.out.println(a); System.out.println(System.currentTimeMillis() - start); } }
運行輸出如清單 15 所示。
清單 15. 運行結果
1000
546
1000
63
兩段代碼執行了完全相同的功能,在每次循環中,整數 1000 乘以 2,然后除以 2。***個循環耗時 546ms,第二個循環耗時 63ms。
替換 switch
關鍵字 switch 語句用于多條件判斷,switch 語句的功能類似于 if-else 語句,兩者的性能差不多。但是 switch 語句有性能提升空間。清單 16 所示代碼演示了 Switch 與 if-else 之間的對比。
清單 16.Switch 示例
public class switchCompareIf { public static int switchTest(int value){ int i = value%10+1; switch(i){ case 1:return 10; case 2:return 11; case 3:return 12; case 4:return 13; case 5:return 14; case 6:return 15; case 7:return 16; case 8:return 17; case 9:return 18; default:return -1; } } public static int arrayTest(int[] value,int key){ int i = key%10+1; if(i>9 || i<1){ return -1; }else{ return value[i]; } } public static void main(String[] args){ int chk = 0; long start=System.currentTimeMillis(); for(int i=0;i<10000000;i++){ chk = switchTest(i); } System.out.println(System.currentTimeMillis()-start); chk = 0; start=System.currentTimeMillis(); int[] value=new int[]{0,10,11,12,13,14,15,16,17,18}; for(int i=0;i<10000000;i++){ chk = arrayTest(value,i); } System.out.println(System.currentTimeMillis()-start); } }
運行輸出如清單 17 所示。
清單 17. 運行結果
172
93
使用一個連續的數組代替 switch 語句,由于對數據的隨機訪問非常快,至少好于 switch 的分支判斷,從上面例子可以看到比較的效率差距近乎 1 倍,switch 方法耗時 172ms,if-else 方法耗時 93ms。
一維數組代替二維數組
JDK 很多類庫是采用數組方式實現的數據存儲,比如 ArrayList、Vector 等,數組的優點是隨機訪問性能非常好。一維數組和二維數組的訪問速度不一樣,一維數組的訪問速度要優于二維數組。在性能敏感的系統中要使用二維數組,盡量 將二維數組轉化為一維數組再進行處理,以提高系統的響應速度。
清單 18. 數組方式對比
public class arrayTest { public static void main(String[] args){ long start = System.currentTimeMillis(); int[] arraySingle = new int[1000000]; int chk = 0; for(int i=0;i<100;i++){ for(int j=0;j<arraySingle.length;j++){ arraySingle[j] = j; } } for(int i=0;i<100;i++){ for(int j=0;j<arraySingle.length;j++){ chk = arraySingle[j]; } } System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); int[][] arrayDouble = new int[1000][1000]; chk = 0; for(int i=0;i<100;i++){ for(int j=0;j<arrayDouble.length;j++){ for(int k=0;k<arrayDouble[0].length;k++){ arrayDouble[i][j]=j; } } } for(int i=0;i<100;i++){ for(int j=0;j<arrayDouble.length;j++){ for(int k=0;k<arrayDouble[0].length;k++){ chk = arrayDouble[i][j]; } } } System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); arraySingle = new int[1000000]; int arraySingleSize = arraySingle.length; chk = 0; for(int i=0;i<100;i++){ for(int j=0;j<arraySingleSize;j++){ arraySingle[j] = j; } } for(int i=0;i<100;i++){ for(int j=0;j<arraySingleSize;j++){ chk = arraySingle[j]; } } System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); arrayDouble = new int[1000][1000]; int arrayDoubleSize = arrayDouble.length; int firstSize = arrayDouble[0].length; chk = 0; for(int i=0;i<100;i++){ for(int j=0;j<arrayDoubleSize;j++){ for(int k=0;k<firstSize;k++){ arrayDouble[i][j]=j; } } } for(int i=0;i<100;i++){ for(int j=0;j<arrayDoubleSize;j++){ for(int k=0;k<firstSize;k++){ chk = arrayDouble[i][j]; } } } System.out.println(System.currentTimeMillis() - start); } }
運行輸出如清單 19 所示。
清單 19. 運行結果
343
624
287
390
***段代碼操作的是一維數組的賦值、取值過程,第二段代碼操作的是二維數組的賦值、取值過程。可以看到一維數組方式比二維數組方式快接近一半時間。而對于數組內如果可以減少賦值運算,則可以進一步減少運算耗時,加快程序運行速度。
提取表達式
大部分情況下,代碼的重復勞動由于計算機的高速運行,并不會對性能構成太大的威脅,但若希望將系統性能發揮到***,還是有很多地方可以優化的。
清單 20. 提取表達式
public class duplicatedCode { public static void beforeTuning(){ long start = System.currentTimeMillis(); double a1 = Math.random(); double a2 = Math.random(); double a3 = Math.random(); double a4 = Math.random(); double b1,b2; for(int i=0;i<10000000;i++){ b1 = a1*a2*a4/3*4*a3*a4; b2 = a1*a2*a3/3*4*a3*a4; } System.out.println(System.currentTimeMillis() - start); } public static void afterTuning(){ long start = System.currentTimeMillis(); double a1 = Math.random(); double a2 = Math.random(); double a3 = Math.random(); double a4 = Math.random(); double combine,b1,b2; for(int i=0;i<10000000;i++){ combine = a1*a2/3*4*a3*a4; b1 = combine*a4; b2 = combine*a3; } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args){ duplicatedCode.beforeTuning(); duplicatedCode.afterTuning(); } }
運行輸出如清單 21 所示。
清單 21. 運行結果
202
110
兩段代碼的差別是提取了重復的公式,使得這個公式的每次循環計算只執行一次。分別耗時 202ms 和 110ms,可見,提取復雜的重復操作是相當具有意義的。這個例子告訴我們,在循環體內,如果能夠提取到循環體外的計算公式,***提取出來,盡可能讓程序 少做重復的計算。
優化循環
當性能問題成為系統的主要矛盾時,可以嘗試優化循環,例如減少循環次數,這樣也許可以加快程序運行速度。
清單 22. 減少循環次數
public class reduceLoop { public static void beforeTuning(){ long start = System.currentTimeMillis(); int[] array = new int[9999999]; for(int i=0;i<9999999;i++){ array[i] = i; } System.out.println(System.currentTimeMillis() - start); } public static void afterTuning(){ long start = System.currentTimeMillis(); int[] array = new int[9999999]; for(int i=0;i<9999999;i+=3){ array[i] = i; array[i+1] = i+1; array[i+2] = i+2; } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args){ reduceLoop.beforeTuning(); reduceLoop.afterTuning(); } }
運行輸出如清單 23 所示。
清單 23. 運行結果
265
31
這個例子可以看出,通過減少循環次數,耗時縮短為原來的 1/8。
布爾運算代替位運算
雖然位運算的速度遠遠高于算術運算,但是在條件判斷時,使用位運算替代布爾運算確實是非常錯誤的選擇。在條件判斷時,Java 會對布爾運算做相當充分的優化。假設有表達式 a、b、c 進行布爾運算“a&&b&&c”,根據邏輯與的特點,只要在整個布爾表達式中有一項返回 false,整個表達式就返回 false,因此,當表達式 a 為 false 時,該表達式將立即返回 false,而不會再去計算表達式 b 和 c。若此時,表達式 a、b、c 需要消耗大量的系統資源,這種處理方式可以節省這些計算資源。同理,當計算表達式“a||b||c”時,只要 a、b 或 c,3 個表達式其中任意一個計算結果為 true 時,整體表達式立即返回 true,而不去計算剩余表達式。簡單地說,在布爾表達式的計算中,只要表達式的值可以確定,就會立即返回,而跳過剩余子表達式的計算。若使用位運算 (按位與、按位或) 代替邏輯與和邏輯或,雖然位運算本身沒有性能問題,但是位運算總是要將所有的子表達式全部計算完成后,再給出最終結果。因此,從這個角度看,使用位運算替 代布爾運算會使系統進行很多無效計算。
清單 24. 運算方式對比
public class OperationCompare { public static void booleanOperate(){ long start = System.currentTimeMillis(); boolean a = false; boolean b = true; int c = 0; //下面循環開始進行位運算,表達式里面的所有計算因子都會被用來計算 for(int i=0;i<1000000;i++){ if(a&b&"Test_123".contains("123")){ c = 1; } } System.out.println(System.currentTimeMillis() - start); } public static void bitOperate(){ long start = System.currentTimeMillis(); boolean a = false; boolean b = true; int c = 0; //下面循環開始進行布爾運算,只計算表達式 a 即可滿足條件 for(int i=0;i<1000000;i++){ if(a&&b&&"Test_123".contains("123")){ c = 1; } } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args){ OperationCompare.booleanOperate(); OperationCompare.bitOperate(); } }
運行輸出如清單 25 所示。
清單 25. 運行結果
63
0
實例顯示布爾計算大大優于位運算,但是,這個結果不能說明位運算比邏輯運算慢,因為在所有的邏輯與運算中,都省略了表達式“”Test_123″.contains(“123″)”的計算,而所有的位運算都沒能省略這部分系統開銷。
使用 arrayCopy()
數據復制是一項使用頻率很高的功能,JDK 中提供了一個高效的 API 來實現它。System.arraycopy() 函數是 native 函數,通常 native 函數的性能要優于普通的函數,所以,僅處于性能考慮,在軟件開發中,應盡可能調用 native 函數。ArrayList 和 Vector 大量使用了 System.arraycopy 來操作數據,特別是同一數組內元素的移動及不同數組之間元素的復制。arraycopy 的本質是讓處理器利用一條指令處理一個數組中的多條記錄,有點像匯編語言里面的串操作指令 (LODSB、LODSW、LODSB、STOSB、STOSW、STOSB),只需指定頭指針,然后開始循環即可,即執行一次指令,指針就后移一個位 置,操作多少數據就循環多少次。如果在應用程序中需要進行數組復制,應該使用這個函數,而不是自己實現。具體應用如清單 26 所示。
清單 26. 復制數據例子
public class arrayCopyTest { public static void arrayCopy(){ int size = 10000000; int[] array = new int[size]; int[] arraydestination = new int[size]; for(int i=0;i<array.length;i++){ array[i] = i; } long start = System.currentTimeMillis(); for(int j=0;j>1000;j++){ System.arraycopy(array, 0, arraydestination, 0, size);//使用 System 級別的本地 arraycopy 方式 } System.out.println(System.currentTimeMillis() - start); } public static void arrayCopySelf(){ int size = 10000000; int[] array = new int[size]; int[] arraydestination = new int[size]; for(int i=0;i<array.length;i++){ array[i] = i; } long start = System.currentTimeMillis(); for(int i=0;i<1000;i++){ for(int j=0;j<size;j++){ arraydestination[j] = array[j];//自己實現的方式,采用數組的數據互換方式 } } System.out.println(System.currentTimeMillis() - start); } public static void main(String[] args){ arrayCopyTest.arrayCopy(); arrayCopyTest.arrayCopySelf(); } }
輸出如清單 27 所示。
清單 27. 運行結果
0
23166
上面的例子顯示采用 arraycopy 方法執行復制會非常的快。原因就在于 arraycopy 屬于本地方法,源代碼如清單 28 所示。
清單 28.arraycopy 方法
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
src – 源數組;srcPos – 源數組中的起始位置; dest – 目標數組;destPos – 目標數據中的起始位置;length – 要復制的數組元素的數量。清單 28 所示方法使用了 native 關鍵字,調用的為 C++編寫的底層函數,可見其為 JDK 中的底層函數。
到此,相信大家對“Java字符串操作、基本運算方法等優化方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。