程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> java中的字符串簡介,字符串的優化以及如何高效率的使用字符串,java字符串

java中的字符串簡介,字符串的優化以及如何高效率的使用字符串,java字符串

編輯:JAVA綜合教程

java中的字符串簡介,字符串的優化以及如何高效率的使用字符串,java字符串


  • 簡介  

  String最為java中最重要的數據類型。字符串是軟件開發中最重要的對象之一,通常,字符串對象在內存中總是占據著最大的空間塊。所以,高效處理字符串,將提高系統的整個性能。  

  在java語言中,String對象可以認為是char數組的衍生和進一步的封裝。它的主要組成部分是:char數組、偏移量和string的長度。char數組表示string的內容,它是string對象所表示字符串的超集。String的真實內容還需要偏移量和長度在這個char數組中進一步定位和截取。(查看java源代碼可以看到char數組、偏移量和長度定義)

  String對象的三個基本特點:

  1、不變性;String對象一旦生成,則不能對它進行改變。String對象的這個特性可以泛指為不變模式,即一個對象的狀態在對象被創建之後就不再發生變化。另外多說一點,不變模式主要作用在當一個對象需要被多線程共享,並且訪問頻繁時,可以省略同步和鎖的等待時間,從而大幅度的提高系統的性能。

  2、針對常量池的優化;當兩個string對象擁有相同的值的時候,他們只引用常量池中同一個拷貝。當同一個字符串反復出現時,可以大幅度的節省內存空間。

  3、類的final的定義。final類的String對象在系統中不可能有任何子類,這是對系統安全性的保護。

  String 類包括的方法可用於檢查序列的單個字符、比較字符串、搜索字符串、提取子字符串、創建字符串副本並將所有字符全部轉換為大寫或小寫等,熟練使用這些方法在企業開發中會有很大的幫助。

  • 截取子字符串

  截取子字符串是java中最常用的操作之一,在java中提供了兩個截取子字符串的方法:

1 substring(int beginIndex, int endIndex)
2 substring(int beginIndex)

  查看substring(int beginIndex, int endIndex)的源碼:

1 public String substring(int beginIndex, int endIndex) { 2 if (beginIndex < 0) { 3 throw new StringIndexOutOfBoundsException(beginIndex); 4 } 5 if (endIndex > value.length) { 6 throw new StringIndexOutOfBoundsException(endIndex); 7 } 8 int subLen = endIndex - beginIndex; 9 if (subLen < 0) { 10 throw new StringIndexOutOfBoundsException(subLen); 11 } 12 return ((beginIndex == 0) && (endIndex == value.length)) ? this 13 : new String(value, beginIndex, subLen); 14 } View Code

  在方法的最後有一個返回一個新建的String對象,查看其構造函數:

1 public String(char value[], int offset, int count) { 2 this.value=value, 3 this.offset=offset, 4 this.count=count 5 } View Code

  在源碼中,這是一個包作用域的構造函數,其目的是為了能高效且快速的共享String內的char數組對象。但是這種通過偏移量來截取字符串的方法中,String原生內容的value數組會被復制到新的子字符串中。設想,如果子字符串長度很短,但是原來字符串長度卻很長,那麼截取的子字符串包含原生字符串中的所有內容,並占據了相應的內存空間,而僅僅通過偏移量和長度來決定自己的實際取值。這種方式提高了運算速度卻浪費了空間,是一種以時間換空間的解決方案。

  實例代碼:

 1 public class Test {
 2 
 3     public static void main(String[] args) {
 4         List<String> hander = new ArrayList<String>();
 5         for (int i = 0; i < 100000; i++) {
 6             HugeStr str = new HugeStr();
 7             //ImpHugeStr str = new ImpHugeStr();
 8             hander.add(str.getSubString(0, 5));
 9         }
10     }
11 
12 }
13 
14 static class ImpHugeStr {
15             private String str = new String(new char[10000]);
16             public String getSubString(int begin,int end){//截取子字符串並且重新生成新的字符串
17                 return new String(str.substring(begin, end));
18             }
19 }
20 static class HugeStr {
21             private String str = new String(new char[10000]);
22             public String getSubString(int begin,int end){//截取子字符串
23                 return str.substring(begin, end);
24             }
25 }

  ImpHugeStr使用沒有內存洩漏的String構造函數重新生成String對象,使得由subString()方法返回的,存在內存洩漏的String對象失去強引用,從而被垃圾回收機制當中垃圾回收,從而保證了系統內存的穩定。

  subString()之所以引起內存洩漏是因為使用了String(int offset,int count,char[] value)構造函數,此構造函數采用以空間換時間的策略。

  以上構造函數在java1.5中采用,在java1.6中采用以下構造函數:

1 public String(char value[], int offset, int count) { 2 if (offset < 0) { 3 throw new StringIndexOutOfBoundsException(offset); 4 } 5 if (count < 0) { 6 throw new StringIndexOutOfBoundsException(count); 7 } 8 // Note: offset or count might be near -1>>>1. 9 if (offset > value.length - count) { 10 throw new StringIndexOutOfBoundsException(offset + count); 11 } 12 this.value = Arrays.copyOfRange(value, offset, offset+count); 13 } View Code

  代碼是采用this.value = Arrays.copyOfRange(value, offset, offset+count),使value失去強引用被垃圾回收,所有也不存在內存溢出的問題。

  • 字符串的分割和查找

  字符串的分割和查找也是字符串處理中最常用的方法之一,字符串分割是將原始字符串根據某一個分割符,切割成一組小的字符串。

  字符串的分割大致有三種方法:

  1、最原始的字符串分割:

  split()是最原始的字符串分割方法,但是它提供了非常強大的字符串分割功能。傳入的參數可以是一個正則表達式,從而實現復雜邏輯的字符串分割。但是,對於簡單字符串的風格,split()的性能卻不盡如人意。所有在性能敏感的系統使用是不可取的。

  2、使用StringTokenizer類分割字符串:

  StringTokenizer是JDK提供的專門用於字符串分割子串的工具類。

  其構造函數:new StringTokenizer(String str,String delim),其中str參數為要處理的分割字符串,delim是分割符。當一個StringTokenizer對象生成後,可以通過nextToken()方法得到下一個要分隔的字符串,通過hasMoreTokens()方法可以知道是否有更多的子字符串需要處理。StringTokenizer的更多具體用法查看API。

  3、自己實現字符串分割方式:

  通過使用String對象的兩個方法:indexOf()和subString()。前面提到subString()是通過以空間換時間的策略,它的執行速度很快,同時indexOf也是一個執行效率特別快的方式。

  實現代碼:

1 String str="";//待分割字符串
2 while(true){
3      String subStr = null;
4      int j = str.indexOf(";");//分割符
5      if(j<0) break;
6      subStr = str.subString(0,j);//截取子字符串
7      str = str.subString(j+1);//剩下待截取子串        
8 }

  三種方式的比較,第一種split功能強大,但是效率最差;第二種StringTokenizer的效率由於split,因此可以使用StringTokenizer的地方一般盡量使用StringTokenizer;第三種執行效率最好,但是可讀性比較差。

  另外,String對象還提供了一個charAt(int index)方法,它返回指定字符串中位於index的字符,它的功能和indexOf()相反,但是它的執行效率同樣十分高。例如,經常在項目組遇到判斷字符串以XX開頭或XX結尾的問題,我們第一想到的是String對象提供的startWith()和endWith()方法,如果改用charAt()方法實現,效率會快很多。例如:"abcd".startWith("abc"),改為"abcd".charAt(1) == "a"&&"abcd".charAt(1) == "b"&&"abcd".charAt(1) == "c"在大量使用的場景下會提高系統效率。

  • StringBuffer和StringBuilder

  String對象是不可變對象,在需要對string對象進行修改操作時,string對象總是會生成新的對象,所有性能會比較差。因此,JDK就提供了專門用於創建和修改字符串的工具,就是StringBuffer和StringBuilder。

  1、String常量的累計操作:

  String對象具有不可變性,因此,一旦String對象實例生成,就不能再改變。如下代碼:

1 String str = "abc"+"de"+"f"+"gh";

  首先,"abc"和"de"兩個字符串生成"abcde"對象,再依次生成"abcdef"和"abcdefgh"對象,理論上這樣做的效率不會高。

  通過StringBuilder實現上訴功能:

1 StringBuffer sb = new StringBuffer();
2 ab.append("abc");
3 ab.append("de");
4 ab.append("f");
5 ab.append("gh");

  對於靜態字符串的連接操作,java在編譯時會進行徹底的優化,將多個連接操作的字符串在編譯時生成一個單獨長的字符串,因此效率會高。

  2、String變量的累加操作:

  String對變量字符串的累加:

1 String str1 = "abc";
2 String str2 = "de";
3 String str3 = "f";
4 String str4 = "gh";
5 String str = str1+str2+str3+str4;

  java編譯時,會會對字符串處理進行一定的優化,對於變量字符串的累加,使用了StringBuilder對象來實現字符串的累加。但是在寫代碼的時候,建議顯示的使用StringBuffer或StringBuilder進行優化。另外在java中類似於"+="或"+"的方法效率低於String對象的concat()方法,而concat()方法又遠遠低於StringBuilder類。因此,在項目中盡量使用StringBuilder來提升效率。

  3、StringBuffer和StringBuilder的選擇

  StringBuffer對幾乎所有方法都做了同步,而StringBuilder幾乎沒做任何同步,同步方法需要消耗一定的系統資源,因此StringBuilder的效率要好於StringBuffer。但是在多線程環境下,StringBuilder無法保證線程安全,因此在不考慮線程安全的情況下使用性能較好的StringBuilder,若系統有線程安全的要求則使用StringBuffer。

  另外,StringBuffer和StringBuilder都可以設置容量大小。

 1 void expandCapacity(int minimumCapacity) {
 2         int newCapacity = value.length * 2 + 2;//擴大容量
 3         if (newCapacity - minimumCapacity < 0)
 4             newCapacity = minimumCapacity;
 5         if (newCapacity < 0) {
 6             if (minimumCapacity < 0) // overflow
 7                 throw new OutOfMemoryError();
 8             newCapacity = Integer.MAX_VALUE;
 9         }
10         value = Arrays.copyOf(value, newCapacity);//數組復制
11     }

  預先評估出容量的大小,能有效的減少數組擴容的操作,從而提升系統性能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved