溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java字符串操作、基本運算方法等優(yōu)化方法是什么

發(fā)布時間:2022-01-06 21:04:37 來源:億速云 閱讀:156 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“Java字符串操作、基本運算方法等優(yōu)化方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習“Java字符串操作、基本運算方法等優(yōu)化方法是什么”吧!

字符串操作優(yōu)化

字符串對象

字符串對象或者其等價對象 (如 char 數(shù)組),在內(nèi)存中總是占據(jù)***的空間塊,因此如何高效地處理字符串,是提高系統(tǒng)整體性能的關(guān)鍵。

String 對象可以認為是 char 數(shù)組的延伸和進一步封裝,它主要由 3 部分組成:char 數(shù)組、偏移量和 String  的長度。char 數(shù)組表示 String 的內(nèi)容,它是 String 對象所表示字符串的超集。String 的真實內(nèi)容還需要由偏移量和長度在這個  char 數(shù)組中進行定位和截取。

String 有 3 個基本特點:

1. 不變性;

2. 針對常量池的優(yōu)化;

3. 類的 final 定義。

不變性指的是 String 對象一旦生成,則不能再對它進行改變。String 的這個特性可以泛化成不變 (immutable)  模式,即一個對象的狀態(tài)在對象被創(chuàng)建之后就不再發(fā)生變化。不變模式的主要作用在于當一個對象需要被多線程共享,并且訪問頻繁時,可以省略同步和鎖等待的時 間,從而大幅提高系統(tǒng)性能。

針對常量池的優(yōu)化指的是當兩個 String 對象擁有相同的值時,它們只引用常量池中的同一個拷貝,當同一個字符串反復(fù)出現(xiàn)時,這個技術(shù)可以大幅度節(jié)省內(nèi)存空間。

下面代碼 str1、str2、str4 引用了相同的地址,但是 str3 卻重新開辟了一塊內(nèi)存空間,雖然 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. 輸出結(jié)果

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 內(nèi)的 char 數(shù)組對象。但在這種通過偏移量來截取字符串的方法中,String 的原生內(nèi)容 value  數(shù)組被復(fù)制到新的子字符串中。設(shè)想,如果原始字符串很大,截取的字符長度卻很短,那么截取的子字符串中包含了原生字符串的所有內(nèi)容,并占據(jù)了相應(yīng)的內(nèi)存空 間,而僅僅通過偏移量和長度來決定自己的實際取值。這種算法提高了速度卻浪費了空間。

下面代碼演示了使用 substring 方法在一個很大的 string 獨享里面截取一段很小的字符串,如果采用 string 的 substring 方法會造成內(nèi)存溢出,如果采用反復(fù)創(chuàng)建新的 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)); } } }

輸出結(jié)果如清單 4 所示。

清單 4. 輸出結(jié)果

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 可以工作是因為它使用沒有內(nèi)存泄漏的 String 構(gòu)造函數(shù)重新生成了 String 對象,使得由  substring() 方法返回的、存在內(nèi)存泄漏問題的 String  對象失去所有的強引用,從而被垃圾回收器識別為垃圾對象進行回收,保證了系統(tǒng)內(nèi)存的穩(wěn)定。

String 的 split 方法支持傳入正則表達式幫助處理字符串,但是簡單的字符串分割時性能較差。

對比 split 方法和 StringTokenizer 類的處理字符串性能,代碼如清單 5 所示。

切分字符串方式討論

String 的 split  方法支持傳入正則表達式幫助處理字符串,操作較為簡單,但是缺點是它所依賴的算法在對簡單的字符串分割時性能較差。清單 5 所示代碼對比了  String 的 split 方法和調(diào)用 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. 運行輸出結(jié)果

39015
16
15

當一個 StringTokenizer 對象生成后,通過它的 nextToken() 方法便可以得到下一個分割的字符串,通過  hasMoreToken 方法可以知道是否有更多的字符串需要處理。對比發(fā)現(xiàn) split 的耗時非常的長,采用 StringTokenizer  對象處理速度很快。我們嘗試自己實現(xiàn)字符串分割算法,使用 substring 方法和 indexOf  方法組合而成的字符串分割算法可以幫助很快切分字符串并替換內(nèi)容。

由于 String 是不可變對象,因此,在需要對字符串進行修改操作時 (如字符串連接、替換),String 對象會生成新的對象,所以其性能相對較差。但是 JVM 會對代碼進行徹底的優(yōu)化,將多個連接操作的字符串在編譯時合成一個單獨的長字符串。

以上實例運行結(jié)果差異較大的原因是 split  算法對每一個字符進行了對比,這樣當字符串較大時,需要把整個字符串讀入內(nèi)存,逐一查找,找到符合條件的字符,這樣做較為耗時。而  StringTokenizer 類允許一個應(yīng)用程序進入一個令牌(tokens),StringTokenizer  類的對象在內(nèi)部已經(jīng)標識化的字符串中維持了當前位置。一些操作使得在現(xiàn)有位置上的字符串提前得到處理。 一個令牌的值是由獲得其曾經(jīng)創(chuàng)建  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 借助于數(shù)據(jù)對象及字符查找算法完成了數(shù)據(jù)分割,適用于數(shù)據(jù)量較少場景。

合并字符串

由于 String 是不可變對象,因此,在需要對字符串進行修改操作時 (如字符串連接、替換),String  對象會生成新的對象,所以其性能相對較差。但是 JVM 會對代碼進行徹底的優(yōu)化,將多個連接操作的字符串在編譯時合成一個單獨的長字符串。針對超大的  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. 運行輸出結(jié)果

375
187
0

雖然***種方法編譯器判斷 String 的加法運行成 StringBuilder 實現(xiàn),但是編譯器沒有做出足夠聰明的判斷,每次循環(huán)都生成了新的 StringBuilder 實例從而大大降低了系統(tǒng)性能。

StringBuffer 和 StringBuilder 都實現(xiàn)了 AbstractStringBuilder  抽象類,擁有幾乎相同的對外借口,兩者的***不同在于 StringBuffer 對幾乎所有的方法都做了同步,而 StringBuilder  并沒有任何同步。由于方法同步需要消耗一定的系統(tǒng)資源,因此,StringBuilder 的效率也好于 StringBuffer。  但是,在多線程系統(tǒng)中,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(); } } }  }

輸出結(jié)果如清單 11 所示。

清單 11. 運行結(jié)果

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 數(shù)據(jù)并沒有按照預(yù)想的方式進行操作。StringBuilder 和 StringBuffer  的擴充策略是將原有的容量大小翻倍,以新的容量申請內(nèi)存空間,建立新的 char  數(shù)組,然后將原數(shù)組中的內(nèi)容復(fù)制到這個新的數(shù)組中。因此,對于大對象的擴容會涉及大量的內(nèi)存復(fù)制操作。如果能夠預(yù)先評估大小,會提高性能。

數(shù)據(jù)定義、運算邏輯優(yōu)化

使用局部變量

調(diào)用方法時傳遞的參數(shù)以及在調(diào)用中創(chuàng)建的臨時變量都保存在棧 (Stack) 里面,讀寫速度較快。其他變量,如靜態(tài)變量、實例變量等,都在堆 (heap) 中創(chuàng)建,讀寫速度較慢。清單 12 所示代碼演示了使用局部變量和靜態(tài)變量的操作時間對比。

清單 12. 局部變量 VS 靜態(tài)變量

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++;//在函數(shù)體內(nèi)定義局部變量 } System.out.println(System.currentTimeMillis() - starttime);  starttime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ b++;//在函數(shù)體內(nèi)定義局部變量 } System.out.println(System.currentTimeMillis() - starttime); } }

運行后輸出如清單 13 所示。

清單 13. 運行結(jié)果

0
15

以上兩段代碼的運行時間分別為 0ms 和 15ms。由此可見,局部變量的訪問速度遠遠高于類的成員變量。

位運算代替乘除法

位運算是所有的運算中最為高效的。因此,可以嘗試使用位運算代替部分算數(shù)運算,來提高系統(tǒng)的運行速度。最典型的就是對于整數(shù)的乘除運算優(yōu)化。清單 14 所示代碼是一段使用算數(shù)運算的實現(xiàn)。

清單 14. 算數(shù)運算

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. 運行結(jié)果

1000
546
1000
63

兩段代碼執(zhí)行了完全相同的功能,在每次循環(huán)中,整數(shù) 1000 乘以 2,然后除以 2。***個循環(huán)耗時 546ms,第二個循環(huán)耗時 63ms。

替換 switch

關(guān)鍵字 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. 運行結(jié)果

172
93

使用一個連續(xù)的數(shù)組代替 switch 語句,由于對數(shù)據(jù)的隨機訪問非???,至少好于 switch 的分支判斷,從上面例子可以看到比較的效率差距近乎 1 倍,switch 方法耗時 172ms,if-else 方法耗時 93ms。

一維數(shù)組代替二維數(shù)組

JDK 很多類庫是采用數(shù)組方式實現(xiàn)的數(shù)據(jù)存儲,比如 ArrayList、Vector  等,數(shù)組的優(yōu)點是隨機訪問性能非常好。一維數(shù)組和二維數(shù)組的訪問速度不一樣,一維數(shù)組的訪問速度要優(yōu)于二維數(shù)組。在性能敏感的系統(tǒng)中要使用二維數(shù)組,盡量 將二維數(shù)組轉(zhuǎn)化為一維數(shù)組再進行處理,以提高系統(tǒng)的響應(yīng)速度。

清單 18. 數(shù)組方式對比

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. 運行結(jié)果

343
624
287
390

***段代碼操作的是一維數(shù)組的賦值、取值過程,第二段代碼操作的是二維數(shù)組的賦值、取值過程??梢钥吹揭痪S數(shù)組方式比二維數(shù)組方式快接近一半時間。而對于數(shù)組內(nèi)如果可以減少賦值運算,則可以進一步減少運算耗時,加快程序運行速度。

提取表達式

大部分情況下,代碼的重復(fù)勞動由于計算機的高速運行,并不會對性能構(gòu)成太大的威脅,但若希望將系統(tǒng)性能發(fā)揮到***,還是有很多地方可以優(yōu)化的。

清單 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. 運行結(jié)果

202
110

兩段代碼的差別是提取了重復(fù)的公式,使得這個公式的每次循環(huán)計算只執(zhí)行一次。分別耗時 202ms 和  110ms,可見,提取復(fù)雜的重復(fù)操作是相當具有意義的。這個例子告訴我們,在循環(huán)體內(nèi),如果能夠提取到循環(huán)體外的計算公式,***提取出來,盡可能讓程序 少做重復(fù)的計算。

優(yōu)化循環(huán)

當性能問題成為系統(tǒng)的主要矛盾時,可以嘗試優(yōu)化循環(huán),例如減少循環(huán)次數(shù),這樣也許可以加快程序運行速度。

清單 22. 減少循環(huán)次數(shù)

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. 運行結(jié)果

265
31

這個例子可以看出,通過減少循環(huán)次數(shù),耗時縮短為原來的 1/8。

布爾運算代替位運算

雖然位運算的速度遠遠高于算術(shù)運算,但是在條件判斷時,使用位運算替代布爾運算確實是非常錯誤的選擇。在條件判斷時,Java  會對布爾運算做相當充分的優(yōu)化。假設(shè)有表達式 a、b、c  進行布爾運算“a&&b&&c”,根據(jù)邏輯與的特點,只要在整個布爾表達式中有一項返回 false,整個表達式就返回  false,因此,當表達式 a 為 false 時,該表達式將立即返回 false,而不會再去計算表達式 b 和 c。若此時,表達式  a、b、c 需要消耗大量的系統(tǒng)資源,這種處理方式可以節(jié)省這些計算資源。同理,當計算表達式“a||b||c”時,只要 a、b 或 c,3  個表達式其中任意一個計算結(jié)果為 true 時,整體表達式立即返回  true,而不去計算剩余表達式。簡單地說,在布爾表達式的計算中,只要表達式的值可以確定,就會立即返回,而跳過剩余子表達式的計算。若使用位運算  (按位與、按位或)  代替邏輯與和邏輯或,雖然位運算本身沒有性能問題,但是位運算總是要將所有的子表達式全部計算完成后,再給出最終結(jié)果。因此,從這個角度看,使用位運算替 代布爾運算會使系統(tǒng)進行很多無效計算。

清單 24. 運算方式對比

public class OperationCompare { public static void booleanOperate(){ long start = System.currentTimeMillis(); boolean a = false; boolean b = true; int c = 0; //下面循環(huán)開始進行位運算,表達式里面的所有計算因子都會被用來計算 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; //下面循環(huán)開始進行布爾運算,只計算表達式 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. 運行結(jié)果

63
0

實例顯示布爾計算大大優(yōu)于位運算,但是,這個結(jié)果不能說明位運算比邏輯運算慢,因為在所有的邏輯與運算中,都省略了表達式“”Test_123&Prime;.contains(“123&Prime;)”的計算,而所有的位運算都沒能省略這部分系統(tǒng)開銷。

使用 arrayCopy()

數(shù)據(jù)復(fù)制是一項使用頻率很高的功能,JDK 中提供了一個高效的 API 來實現(xiàn)它。System.arraycopy() 函數(shù)是 native  函數(shù),通常 native 函數(shù)的性能要優(yōu)于普通的函數(shù),所以,僅處于性能考慮,在軟件開發(fā)中,應(yīng)盡可能調(diào)用 native 函數(shù)。ArrayList  和 Vector 大量使用了 System.arraycopy  來操作數(shù)據(jù),特別是同一數(shù)組內(nèi)元素的移動及不同數(shù)組之間元素的復(fù)制。arraycopy  的本質(zhì)是讓處理器利用一條指令處理一個數(shù)組中的多條記錄,有點像匯編語言里面的串操作指令  (LODSB、LODSW、LODSB、STOSB、STOSW、STOSB),只需指定頭指針,然后開始循環(huán)即可,即執(zhí)行一次指令,指針就后移一個位 置,操作多少數(shù)據(jù)就循環(huán)多少次。如果在應(yīng)用程序中需要進行數(shù)組復(fù)制,應(yīng)該使用這個函數(shù),而不是自己實現(xiàn)。具體應(yīng)用如清單 26 所示。

清單 26. 復(fù)制數(shù)據(jù)例子

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];//自己實現(xiàn)的方式,采用數(shù)組的數(shù)據(jù)互換方式 } } System.out.println(System.currentTimeMillis() - start); }  public static void main(String[] args){ arrayCopyTest.arrayCopy(); arrayCopyTest.arrayCopySelf(); } }

輸出如清單 27 所示。

清單 27. 運行結(jié)果

0
23166

上面的例子顯示采用 arraycopy 方法執(zhí)行復(fù)制會非常的快。原因就在于 arraycopy 屬于本地方法,源代碼如清單 28 所示。

清單 28.arraycopy 方法

public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);

src &ndash; 源數(shù)組;srcPos &ndash; 源數(shù)組中的起始位置; dest &ndash; 目標數(shù)組;destPos &ndash; 目標數(shù)據(jù)中的起始位置;length  &ndash; 要復(fù)制的數(shù)組元素的數(shù)量。清單 28 所示方法使用了 native 關(guān)鍵字,調(diào)用的為 C++編寫的底層函數(shù),可見其為 JDK 中的底層函數(shù)。

到此,相信大家對“Java字符串操作、基本運算方法等優(yōu)化方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI