淺談StringBuilder類的capacity()方法和length()方法的一些小坑

今天在做項目的過程中遇見一個StringBuilder.delete()刪除得不到自己期望結果問題,一個截取字符串的問題,總得不到自己所期望的答案:

問題如下:

stringBuilder.delete(stringBuilder.capacity() - 5, stringBuilder.capacity());

此句代碼要麼報錯,要麼多刪,要麼少刪,也有時候正確。也有時候得不到自己所想要的字符串;

簡單的測試capacity()方法和length()方法的區別如下:

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefgh2321");
System.out.println("length:" + stringBuilder.length());
System.out.println("capacity:" + stringBuilder.capacity());
System.out.println("length截取:" + stringBuilder.delete(stringBuilder.length() - 3,stringBuilder.length()));
System.out.println("capacity截取:" + stringBuilder.delete(stringBuilder.capacity() - 7,stringBuilder.capacity()));
System.out.println("capacity截取:" + stringBuilder.delete(stringBuilder.capacity() - 5,stringBuilder.capacity()));

代碼如上:

輸出:

最後查看源碼,

StringBuilder類繼承於AbstractStringBuilder抽象類:

在AbstractStringBuilder抽象類中,放入進去的字符串存儲於char[] value 數組中,count為存進去的字符數目,

使用capacity()方法得到的為 value數組的長度,length()方法得到的為count,也就是字符串的實際長度。

在初始化StringBuilder時候,也就是new StringBuilder()時候,會初始化一個char[16]大小的char數據來存儲字符串,如果字符串增加之後,會進行擴容。

當然,如果 new StringBuilder(“213123”);會在字符串的長度的增加16作為初始數組char[] value的大小,

最後:

希望大傢在使用capacity()方法和length()方法時要註意選擇:否則會導致意想不到的坑。

StringBuilder初始化的大小對性能的影響

StringBuilder 是一個可以動態增加自身數據長度的類,其默認長度(capacity屬性)為16。

它有一個構造函數,可以指定其容器長度。當數據量小時,指定長度意義不大,但是當數據量比較大時,指定長度會對性能產生顯著影響。

本文通過一個小示例驗證其對性能產生的影響

代碼如下:

public class StringBuilderTest {
 public static void main(String[] args) throws Exception {
  // 前兩行分別是獲取運行次數和StringBuilder的初始化長度
  int times = args.length > 0 ? Integer.parseInt(args[0]) : 100;
  int length = args.length > 1 ? Integer.parseInt(args[1]) : 0;
  
  // 運行 times 次的 test(length)方法
  long t1 = System.currentTimeMillis();
  for (int i = 0; i < times; i++)
   test(length);
  long t2 = System.currentTimeMillis();
  
  // 輸出單次運行時間 
  System.out.printf("Time taken: %d ms.\n", (t2 - t1) / times);
 }

 // 這個方法隻是單純地做循環向StringBuilder中添加數據。
 static int test(int length) {
  StringBuilder sb = new StringBuilder(length);
  for (int i = 0; i < 10000000; i++)
   sb.append(i + "");
  return sb.length();
 }
}

以下是編碼後,分配2GB內存,在控制臺測試運行100次的運行結果。

$ java -Xmx2g -Xms2g StringBuilderTest 100 0

Time taken: 273 ms.

$ java -Xmx2g -Xms2g StringBuilderTest 100 73000000

Time taken: 205 ms.

性能差距約為30%

結論

當在使用StringBuilder處理大數據的時候,如果我們可以預知或者以很小的性能損失就能獲得數據的大小時,提前指定StringBuilder的長度可顯著提高處理速度。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: