你了解Java中String的substring函數嗎?


你了解Java中String的substring函數嗎?

Java中的substring函數是我們經常使用的一個函數,用來截取當前字符串的子串,定義如下:

public final class String{
    public String substring(int beginIndex);
    public String substring(int beginIndex, int endIndex);
}

使用及聲明都非常簡單,但是你了解其中的細節嗎?

我們再看一下substring的實現:

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

在第12行返回了一個新的字符串,傳入了三個參數:offset,count,以及原來String對象的value(char[])。

繼續看第12行String的構造函數:

String(int offset, int count, char value[]) {
        this.value = value;
        this.offset = offset;
        this.count = count;
}

這是一個Package內部的方法,非public,注意他直接將傳入的char[]給抓住了,定義了起始位置,以及長度。

我理解他設計的初衷是為了節省內存,新的字符串還依然抓着老的字符串value的引用,只是重新定義起始位置和長度

再補充一下String類的字段聲明,可以看得更清楚一點:

public final class String{
   /** The value is used for character storage. */
    private final char value[];

    /** The offset is the first index of the storage that is used. */
    private final int offset;

    /** The count is the number of characters in the String. */
    private final int count;

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}

---------------------------------------------

華麗的分割線下是我的使用場景:

逐行讀取一個非常大的文本文件,每一行的長度比較大,提取其中的小部分(使用了substring函數),大約每行提取10個字,行數很多(整個文本可能會有幾十M或更多)。

讀取文件完畢后我理解的內存消耗不會很大,但是完全出乎我的想象,內存消耗很大,甚至會outofmemory.

我的代碼:

List<String> results = new ArrayList<String>();
InputStream stream = new FileInputStream(filePath);
BufferedReader bufferredReader = new BufferedReader(
                new InputStreamReader(stream));
while (true) {
    String line = bufferredReader.readLine();
if (line == null) {
                break;
    }
results.add(line.substring(
10, 20)); }

Why?在查看了String的substring函數以及多方Google之后,終於明白了。

原來新構建的String依然抓着每一行的文本,只是調整了offset和length,不是我們所理解的只抓着一個小文本,原來的長文本被GC回收這么回事。

怎么辦吶?只需要改一句:

List<String> results = new ArrayList<String>();
InputStream stream = new FileInputStream(filePath);
BufferedReader bufferredReader = new BufferedReader(
                new InputStreamReader(stream));
while (true) {
    String line = bufferredReader.readLine();
    if (line == null) {
                break;
    }

    results.add(new String(line.substring(10, 20)));
}

構建一個新的String,將字串傳入,這樣與原始字符串就沒有關系了。所以在使用substring的時候,必須要注意使用場景。

最后再提醒一個,String的split函數返回的子串也是如此。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM