Android研發中對String的思考

標記:http://blog.csdn.net/zl18603543572/article/details/52614585java

 

一、經常使用建立方式思考:
      

String text = "this is a test text ";

 上面這一句話其實是執行了三件事 
        一、聲明變量 String text;
        二、在內存中開闢空間 (內存空間一)
        三、將變量的text的引用指向開闢的內存空間git

    當有 正則表達式

text = "this is a change text";

  這一句話執行了兩件事
        一、在內存中開闢空間
        二、將變量text 的引用指向 新開闢的內存空間
        三、內存空間一此時依然存在,這就是說明了String的不可變性小程序

    測試實踐一:
      api

String text = "oo";
 //循環體系將字符串拼接
for (int i = 0; i < 90000; i++) {
      text+=i;
}

        這段小程序在個人應用中執行了8s的時間 ,太長了,緣由很簡單,就是不斷的在重複建立新的對象與內存空間,同時還要不時的釋放未使用的內存空間數組

    測試實踐二:緩存

String text = "oo";
//建立字符緩衝區
StringBuilder builder = new StringBuilder(text);
//循環體系將字符串拼接
for (int i = 0; i < 100000; i++) {
      builder.append(i);
 }

        執行這一小段代碼,執行的次數遠遠超於實踐一中,然而時間只使用了 30 ms,大優化了性能,緣由只是由於其在只是開闢了一個緩存空間,每次只把新的數據添加到緩存中。安全

二、建立String 的構造簡析

     2.一、String string = new String ();


     這種方法是建立了一個空的String,在內存空間中開闢了一個空內容的地址,實際上也沒有什麼用,源碼中有這樣的描述 app

/**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.value = "".value;
    }

 雖然是空的String(是""而不是null,「」出是佔有內存空間,只不過內容是空的) ,但在其構造方法中依然  調用了value方法,也就是空字符串的value,
 所謂value  
 ide

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

 也就是說當String一旦建立後,就會被轉爲 相應的字節數組 

 2.二、String string = new String (" test "); 

     這種方法來建立String,與咱們經常使用建立方式分析中的思路一至,聲明變量,開闢空間,賦值引用 

     源碼中這樣操做

public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

 毫無疑問,它的構造方法中首先將其轉爲了對應的字節數組中的數據。
 同時 獲取其對應的Hash值 

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

2.三、將char數組中的內容構造爲String類型

char[] chars = new char[]{"q","e","e","q","e","e"};
        //將char數組中的內容構造爲String類型
        String string = new String(chars);

        //構造的字符串爲 qeeqee

 

這裏是依賴字符數組來建立構造String  ,從源碼的角度來看,毫無疑問在其對應的構造中首先將其轉爲了對應字符數組 
 

public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }

 能夠看到這裏並無直接經過 Arrays的copyOf建立了一個新的字符數組,並賦值 於this.value,

public static char[] copyOf(char[] original, int newLength) {
            char[] copy = new char[newLength];
            System.arraycopy(original, 0, copy, 0,
                            Math.min(original.length, newLength));
            return copy;
        }

2.四、 將字符數組中的部分數據構造爲String 

char[] chars = new char[]{"q","e","e","q","e","e"};
        //構造字符串
        String string = new String(chars,0,2);

        //構造的字符串爲 qe 
        //也就是說將字符數組中的部分字符 構形成String

從其構造源碼來看 :

public String(char value[], int offset, int count) {
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
             if (count <= 0) {
                 if (count < 0) {
                    throw new StringIndexOutOfBoundsException(count);
                }
            if (offset <= value.length) {
                    this.value = "".value;
                    return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }

    也就是說當咱們經過這種方法來構造String 的時候 , 傳入的參數中構造字符起始位置 offset小於零,那麼將會出異常,構造字符個數 count 小於0,那麼意味取出的字符數組中的長度爲0,也就是構造一個 ""字符串,同時賦值其字符數組,

        當構造起始位置 字符個婁都不小於0的時候 ,當起始位置與取出的字符長度設置不合理(所謂不合理指的是 例 字符數組長度爲 5,構造字符串的時候傳入的 起始位置 爲3 ,構造長度爲5,也就是說在字符數組中從3號位置取值,向後延取5個值,原字符數組長度必然不夠)的時候,拋出異常
        當傳入的參數合理的時候,則經過Arrays的copyOfRange方法建立一個新的字符數組空間,並賦值 this.value 

public static char[] copyOfRange(char[] original, int from, int to) {
            int newLength = to - from;
            if (newLength < 0)
                throw new IllegalArgumentException(from + " > " + to);
            char[] copy = new char[newLength];
            System.arraycopy(original, from, copy, 0,
                            Math.min(original.length - from, newLength));
            return copy;
        }

2.六、 經過 unicode數組來構造String

int[] charIntArray = new int[]{67,68,69,70};

String  string = new String (charIntArray,0,charIntArray.length);

//對應的字符串 CEDF

從源碼角度來看:(具體的關於unicode將在下文中簡述)

public String(int[] codePoints, int offset, int count) {
                if (offset < 0) {
                    throw new StringIndexOutOfBoundsException(offset);
                }
                if (count <= 0) {
                    if (count < 0) {
                        throw new StringIndexOutOfBoundsException(count);
                    }
                    if (offset <= codePoints.length) {
                        this.value = "".value;
                        return;
                    }
                }
                // Note: offset or count might be near -1>>>1.
                if (offset > codePoints.length - count) {
                    throw new StringIndexOutOfBoundsException(offset + count);
                }

                final int end = offset + count;

                // Pass 1: Compute precise size of char[]
                int n = count;
                for (int i = offset; i < end; i++) {
                    int c = codePoints[i];
                    if (Character.isBmpCodePoint(c))
                        continue;
                    else if (Character.isValidCodePoint(c))
                        n++;
                    else throw new IllegalArgumentException(Integer.toString(c));
                }

                // Pass 2: Allocate and fill in char[]
                final char[] v = new char[n];

                for (int i = offset, j = 0; i < end; i++, j++) {
                    int c = codePoints[i];
                    if (Character.isBmpCodePoint(c))
                        v[j] = (char)c;
                    else
                        Character.toSurrogates(c, v, j++);
                }

                this.value = v;
            }

2.七、將字節數組構建爲String

byte[] newByte = new byte[]{4,5,6,34,43};
        //構造字符 
        String string = new String (newByte,0,newByte,"UTF-8");
        //參數一 對應的字節數組
        //參數二 參數三 構造字符串的字節範圍
        //參數四 構造字符串的編碼方式 這裏使用的 爲UTF - 8;

 從其源碼的角度來看

public String(byte bytes[], int offset, int length, String charsetName)
                throws UnsupportedEncodingException {
                    if (charsetName == null)
                        throw new NullPointerException("charsetName");
                    checkBounds(bytes, offset, length);
                    this.value = StringCoding.decode(charsetName, bytes, offset, length);
            }

若是傳入的編碼方式爲空,則直接拋出異常

而下面的checkBounds方法只是作了一些安全性檢驗 

private static void checkBounds(byte[] bytes, int offset, int length) {
                if (length < 0)
                    throw new StringIndexOutOfBoundsException(length);
                if (offset < 0)
                    throw new StringIndexOutOfBoundsException(offset);
                if (offset > bytes.length - length)
                    throw new StringIndexOutOfBoundsException(offset + length);
            }

 而後經過 方法StringCoding.decode 方法建立了一個新的字符數組,並賦值與 this.value 

2.八、 將字節數組中的一部分數據構建爲String

byte[] newByte = new byte[]{4,5,6,34,43};
        //構造字符 
        String string = new String (newByte,"UTF-8");

 

三、經常使用處理String操做的方法分析

    3.一、 取出一個String中的指定角標下的一個字符 

String text = "thisisapicture";
        //取出角標索引爲0位置的字符
        char cahrString = text . charAt(0);

        //這裏取出來的字符爲 t

從源碼角度來看

public char charAt(int index) {
            if ((index < 0) || (index >= value.length)) {
                throw new StringIndexOutOfBoundsException(index);
            }
            return value[index];
        }

從其源碼看來,當咱們剛剛建立一個String 對象的時候,String對象的內容則會被轉爲內存空間中的相應的字數組,而在這裏,則是從其對應的內存數組中拿到對應角標下的相應字符 

3.二、 對字符串的分割 

        3.2.一、 split方法進行分割

String textString = "q,q,w,e,e,r,f,g3,g";
        //將字符串以「,」爲基準進行分割
        String[] stringArray = textString.split(",");
        //獲得相應的字符串數組 
        // [q, q, w, e, e, r, f, g3, g]

    從源碼的角度來看 

public String[] split(String regex) {
            return split(regex, 0);
        }

    實際中調用了兩個參數的重載方法

public String[] split(String regex, int limit) {
            /* fastpath if the regex is a
             (1)one-char String and this character is not one of the
                RegEx's meta characters ".$|()[{^?*+\\", or
             (2)two-char String and the first char is the backslash and
                the second is not the ascii digit or ascii letter.
             */
            char ch = 0;
            if (((regex.value.length == 1 &&
                     ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
                     (regex.length() == 2 &&
                      regex.charAt(0) == '\\' &&
                      (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
                      ((ch-'a')|('z'-ch)) < 0 &&
                      ((ch-'A')|('Z'-ch)) < 0)) &&
                    (ch < Character.MIN_HIGH_SURROGATE ||
                     ch > Character.MAX_LOW_SURROGATE))
                {
                    int off = 0;
                    int next = 0;
                    boolean limited = limit > 0;
                    ArrayList<String> list = new ArrayList<>();
                    while ((next = indexOf(ch, off)) != -1) {
                        if (!limited || list.size() < limit - 1) {
                            list.add(substring(off, next));
                            off = next + 1;
                        } else {    // last one
                            //assert (list.size() == limit - 1);
                            list.add(substring(off, value.length));
                            off = value.length;
                            break;
                        }
                    }
                    // If no match was found, return this
                    if (off == 0)
                        return new String[]{this};

                    // Add remaining segment
                    if (!limited || list.size() < limit)
                        list.add(substring(off, value.length));

                    // Construct result
                    int resultSize = list.size();
                    if (limit == 0) {
                        while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                            resultSize--;
                        }
                    }
                    String[] result = new String[resultSize];
                    return list.subList(0, resultSize).toArray(result);
                }
                return Pattern.compile(regex).split(this, limit);
            }

    當看到這段源碼的時候,有點頭疼,由於它有點長,其實仔細一看,也不過如此

    能夠看到方法內有一個if,若是條件爲true,那麼就使用indexOf()打開循環體系,判斷後substring()截取,
         //第一步部分:當regex的長度爲1且不是「.$|()[{^?*+\\」中的時,爲真
            (regex.value.length == 1 &&".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1)
        //第二部分:當長度爲2時且第一個字符爲「\」轉義字符,第二個字符不是字符0-9 a-z A-Z 以及utf-16之間的字符
       
        從if能夠看出若是regex內容爲一個非正則匹配符或者是轉之後的特殊字符時,採用indexOf()+substring()處理,不然使用正則表達式   Pattern.compile(regex).split(this, limit)

        也就是說,咱們在使用Split方法對String字符串進行分割的時候,不只能夠以一個普通的字符爲標準進行分割,還可使用一個正則表達式進行分割

        而當使用的分割符號正好爲正則表達式中的某些符號的時候,須要正則轉義纔可以獲得正確的結果 

3.3 獲取字符串中指定角標字符的Unicode編碼 

String text = "ABCD";
    //獲取0號位置也就是'A'字符的編碼
    int uniCode = text.codePointAt(0);

 從源碼來看 :

public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }

實際的操做體則是Character這個類在發揮做用:Character.codePointAtImpl(value, index, value.length)

也就是直接將String text對應的字符數組,以及要獲取字符角標,以及字符數組長度傳參

        Unicode給世界上每一個字符分配了一個編號,編號範圍從0x000000到0x10FFFF。

                編號範圍在0x0000到0xFFFF之間的字符,爲經常使用字符集,稱BMP(Basic Multilingual Plane)字符。

                 編號範圍在0x10000到0x10FFFF之間的字符叫作增補字符(supplementary character)。

        Unicode主要規定了編號,但沒有規定若是把編號映射爲二進制,UTF-16是一種編碼方式,或者叫映射方式,它將編號映射爲兩個或四個字節,對BMP字符,它直接用兩個字節表示,對於增補字符,使用四個字節,前兩個字節叫高代理項(high surrogate),範圍從0xD800到0xDBFF,後兩個字節叫低代理項(low surrogate),範圍從0xDC00到0xDFFF,UTF-16定義了一個公式,能夠將編號與四字節表示進行相互轉換。

    Java內部採用UTF-16編碼,char表示一個字符,但只能表示BMP中的字符,對於增補字符,須要使用兩個char表示,一個表示高代理項,一個表示低代理項。

    使用int能夠表示任意一個Unicode字符,低21位表示Unicode編號,高11位設爲0。整數編號在Unicode中通常稱爲代碼點(Code Point),表示一個Unicode字符,與之相對,還有一個詞代碼單元(Code Unit)表示一個char。

    而在Character這個類中,對應的靜態操做方法實則爲:

static int codePointAtImpl(char[] a, int index, int limit) {
        char c1 = a[index];
        if (isHighSurrogate(c1) && ++index < limit) {
            char c2 = a[index];
            if (isLowSurrogate(c2)) {
                return toCodePoint(c1, c2);
            }
        }
        return c1;
    }

        實際 在這個方法中的第一步是從text對應的字符數組中取出對應的角標的字符,若是不符合其中if的條件,那麼將直接獲取的是字符自己,也就是字符自己的unicode編碼是自己
        在if的判斷條件中.執行判斷取出的字符是否在isHighSurrogate這個方法所限定的範圍內,

public static boolean isHighSurrogate(char ch) {
        // Help VM constant-fold; MAX_HIGH_SURROGATE + 1 == MIN_LOW_SURROGATE
        return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
    }

     MIN_HIGH_SURROGATE utf-16 編碼中的 unicode 高代理項代碼單元的最小值。高代理項也稱爲前導代理項。
    MAX_HIGH_SURROGATE utf-16 編碼中的 unicode 高代理項代碼單元的最大值
    也就是至關於查找一下咱們所要獲取字符是否在 unicode定義的範圍內 ,也就是說判斷 字符是否爲 0xD800到0xDBFF 中的高代理項字符

    若是在,則執行 isLowSurrogate方法的限定判斷 

public static boolean isLowSurrogate(char ch) {
        return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1);
    }

    此方法能夠理解爲 肯定給定char值是否爲一個Unicode低代理項代碼單元
    也能夠理解爲 判斷對應字符是否在  0xDC00到0xDFFF 範圍的 低代理項。
    若是 在,則執行toCodePoint方法計算其對應的Unicode值,也就是根據高代理項high和低代理項low生成代碼單元

public static int toCodePoint(char high, char low) {
        // Optimized form of:
        // return ((high - MIN_HIGH_SURROGATE) << 10)
        //         + (low - MIN_LOW_SURROGATE)
        //         + MIN_SUPPLEMENTARY_CODE_POINT;
        return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
                                       - (MIN_HIGH_SURROGATE << 10)
                                       - MIN_LOW_SURROGATE);
    }

3.四、獲取字符串中指定角標索引前一個元素的代碼點

String textString = "ABCDEF";
        //這裏是獲取三號位置前面一位,也就是二號位 C 的uniCode編碼 
        int uniCode = textString.codePointBefore(3);

    從源碼來看:實際實操做的仍是字符串對應的字符數組,操做方法依然是Character這個類所定義的靜態方法

public int codePointBefore(int index) {
            int i = index - 1;
            if ((i < 0) || (i >= value.length)) {
                throw new StringIndexOutOfBoundsException(index);
            }
            return Character.codePointBeforeImpl(value, index, 0);
        }

    而在Character這個類中的方法中則是直接從對應的字符數組中取出角標前一位的字符,而後斷定是否在編碼區中的 高 低代理區,若是 在,那麼就計算返回對應的編碼

static int codePointBeforeImpl(char[] a, int index, int start) {
        char c2 = a[--index];
        if (isLowSurrogate(c2) && index > start) {
            char c1 = a[--index];
            if (isHighSurrogate(c1)) {
                return toCodePoint(c1, c2);
            }
        }
        return c2;
        }

3.五、獲取字符串中指定範圍內的字符串Unicode代碼點數

String textString = "ABCDEF";
        //這裏獲取的是整個字符串所對應的 代碼點數
        int uniCodeCount = textString.codePointCount(0, textString.length());

從源碼角度來看 實際仍是在操做String對應的字符數組,負責操做的是 Character這個類

public int codePointCount(int beginIndex, int endIndex) {
        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
            throw new IndexOutOfBoundsException();
        }
            return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
        }

這裏直接傳入字符串對應的字符數組,以及範圍的開始點,範圍長度 

static int codePointCountImpl(char[] a, int offset, int count) {
            int endIndex = offset + count;
            int n = count;
            for (int i = offset; i < endIndex; ) {
                if (isHighSurrogate(a[i++]) && i < endIndex &&
                    isLowSurrogate(a[i])) {
                    n--;
                    i++;
                }
            }
                return n;
        }

 能夠看出來 在這裏面也是一個循環判斷每個字符是否在高代理項區與低代理項區,而後進行計數

 

3.六、比較兩個字符串是否相等 

String text1 = "ABCDEF";
        String text2 = "ABCDEFG";
        //比較
        int compareNumber = text1.compareTo(text2);

    其實就是依次比較兩個字符串ASC碼。若是兩個字符的ASC碼相等則繼續後續比較,不然直接返回兩個ASC的差值。若是兩個字符串徹底同樣,則返回0

    從源碼角度來看:

public int compareTo(String anotherString) {
            int len1 = value.length;
            int len2 = anotherString.value.length;
            int lim = Math.min(len1, len2);
            char v1[] = value;
            char v2[] = anotherString.value;

            int k = 0;
            while (k < lim) {
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;
                }
                k++;
            }
            return len1 - len2;
        }

        在這裏先字符串自己對應的字符數組的長度,而後再經過 方法anotherString.value.length方法獲取比較對象String的長度

3.7 忽略大小寫比較兩個字符串

String text1 = "ABCDEF";
        String text2 = "ABCDEFG";
        //比較
        int compareNumber = text1.compareToIgnoreCase(text2);

從源碼角度來看 

public int compareToIgnoreCase(String str) {
            return CASE_INSENSITIVE_ORDER.compare(this, str);
        }

        這裏直接使用了CASE_INSENSITIVE_ORDER的compare方法來比較兩個字符串,而CASE_INSENSITIVE_ORDER是String類中定義的一個比較器
        (1.2版本開始使用)
        public static final Comparator<String> CASE_INSENSITIVE_ORDER

 

3.8 將兩個字符串拼接爲一個字符串

String text1 = "ABCDEF";
        String text2 = "ABCDEFG";
        //這裏將 text2 拼接到text1後面 
        String newString  = text1.concat(text2);

     將兩個字符串拼接到一塊兒,可使用 text1+text2這個方法,只不過是在性能上有一點點的不合適 

      從源碼角度來看

public String concat(String str) {
            int otherLen = str.length();
            if (otherLen == 0) {
                return this;
            }
            int len = value.length;
            char buf[] = Arrays.copyOf(value, len + otherLen);
            str.getChars(buf, len);
            return new String(buf, true);
        }

        第一步 在這裏先進行字符串斷定,若是要拼接的字符串是"",那麼拼接後依然是自己,所在這裏直接return,
        第二步 經過 Arrays.copyOf方法來創新一個新的指定大小的數組

public static char[] copyOf(char[] original, int newLength) {
                char[] copy = new char[newLength];
                System.arraycopy(original, 0, copy, 0,
                                 Math.min(original.length, newLength));
                return copy;
            }
            //這裏建立一個新長度的數組空間並將原來的字符數組COPY進去

        第三步 經過 String 的 getChars方法將 兩個字符串對應的字符數組拼接
        第四步 構造新的字符串返回

3.9 查找一個字符在字符串中第一次出現的位置 

String text = "ABCD ";
        //查找 "A" 在字符串text中第一次出現的位置 
        int index = text.indexOf("A");

從源碼角度來看

public int indexOf(String str) {
            return indexOf(str, 0);
        }

        //再看indexOf(String str, int fromIndex)方法

 public int indexOf(String str, int fromIndex) {
         return indexOf(value, 0, value.length,
                    str.value, 0, str.value.length, fromIndex);
        }


        上述indexfOf(String str)方法中調用indexOf(String str,int fromIndex)方法,而後傳入0,也就是說將從String的開始位置查找指定字符在字符串中出現的位置,

        而在indexOf(String str,int fromIndex)這個方法中則調用了indexOf的多參數方法,這時分別傳入 
            value   父字符串的字符數組
            0       父字符串的有效字符起始角標索引 這裏傳入0,也就是整個父字符串均可做爲操做對象  
            value.length 父字符串的字符數組長度
            str.value  子字符串的字符數組
            0          子字符串的有效字符起始角標索引
            str.value.length 子字符串的字符數組長度
            fromIndex   在父字符串的查找的開始位置 


        再看多參數方法

static int indexOf(char[] source, int sourceOffset, int sourceCount,
                char[] target, int targetOffset, int targetCount,
                int fromIndex) {
            if (fromIndex >= sourceCount) {
                return (targetCount == 0 ? sourceCount : -1);
            }
            if (fromIndex < 0) {
                fromIndex = 0;
            }
            if (targetCount == 0) {
                return fromIndex;
            }

            char first = target[targetOffset];
            int max = sourceOffset + (sourceCount - targetCount);

            for (int i = sourceOffset + fromIndex; i <= max; i++) {
                /* Look for first character. */
                if (source[i] != first) {
                    while (++i <= max && source[i] != first);
                }

                /* Found first character, now look at the rest of v2 */
                if (i <= max) {
                    int j = i + 1;
                    int end = j + targetCount - 1;
                    for (int k = targetOffset + 1; j < end && source[j]
                            == target[k]; j++, k++);

                    if (j == end) {
                        /* Found whole string. */
                        return i - sourceOffset;
                    }
                }
            }
            return -1;
        }

在這個方法中,參數最多,那麼說明處理的方法就在這裏面 
        先分析參數 
            char[] source,         父String 對應字符數組 
            int sourceOffset,      父String 被使用起始索引
            int sourceCount,       父String 對應字符數組長度
            char[] target,         子String 對應字符數組
            int targetOffset,      子String 被使用超始索引
            int targetCount,       子String 對應字符數組長度
            int fromIndex           檢索起始位置 

        從上到下 
            if (fromIndex >= sourceCount) {
                    return (targetCount == 0 ? sourceCount : -1);
                }
            若是查找子String標識的在父View中的起始位置 大於等於 父String的長度
            則終止操做,返回0 或者 -1 
            當子字符串的長度爲0的時候,則返加0,此時子字符串爲」「 內容爲空的空字符串
            當子字符串的長度不爲0的時候,返回-1,也就是說沒有查找到


            if (fromIndex < 0) {
                fromIndex = 0;
            }  
            當檢索起始位置小於0,將起始位置默認設爲0

            if (targetCount == 0) {
                return fromIndex;
            }
            當檢索的子String對應的字符數組長度爲0時,返回傳入的檢索位置 

            char first = target[targetOffset];
            取出檢索子String的第一個字符 

            int max = sourceOffset + (sourceCount - targetCount);
            父String被檢索起始位 + (父String對應字符數組長度 - 子String對應字符數組長度)

            for (int i = sourceOffset + fromIndex; i <= max; i++) {
                。。。。
            }
            若是循環體系中的條件沒有被知足,那說明沒有查找到對應的字符,返回-1 

            在循環體系中 
             /* Look for first character. */
                if (source[i] != first) {
                    while (++i <= max && source[i] != first);
                }
            上面這段代碼讓我感受到無語
            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                    int j = i + 1;
                    int end = j + targetCount - 1;
                    for (int k = targetOffset + 1; j < end && source[j]
                            == target[k]; j++, k++);

                    if (j == end) {
                        /* Found whole string. */
                        return i - sourceOffset;
                    }
                }

 

3.10 判斷一個字符串中是否包含指定的某一個字符 

String text = "ABCE";
        //判斷text中是否包含 "A"
        boolean isContans = text.contains("A");

從源碼的角度來看:

public boolean contains(CharSequence s) {
            return indexOf(s.toString()) > -1;
        }

    能夠看到其實實際中仍是String 的indexOf在發揮實際的做用,這裏調用的indexOf(String str)方法,當查找到對應的字符時,會返回這個字符在字符串中的角標索引,角標索引從0 開始 確定大於-1,與-1相比較,返回true
        若是沒有找到,indexOf方法返回的是 -1,最終contains方法返回的是 false,若是傳入的是 ""空字符串,indexOf方法會返回0,最終contains方法返回的是true 

3.11 比較字符串 到指定的CharSequence 序列是否相同 

(2016-9-22 18:43 更新)

String text1 = "ABCDEF";
        //比較序列
        boolean isExit = text1.contentEquals("AABCDEF");

從源碼的角度來看

public boolean contentEquals(StringBuffer sb) {
            return contentEquals((CharSequence)sb);
        }
        public boolean contentEquals(CharSequence cs) {
            // Argument is a StringBuffer, StringBuilder
            if (cs instanceof AbstractStringBuilder) {
                if (cs instanceof StringBuffer) {
                    synchronized(cs) {
                       return nonSyncContentEquals((AbstractStringBuilder)cs);
                    }
                } else {
                    return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            }
            // Argument is a String
            if (cs instanceof String) {
                return equals(cs);
            }
            // Argument is a generic CharSequence
            char v1[] = value;
            int n = v1.length;
            if (n != cs.length()) {
                return false;
            }
            for (int i = 0; i < n; i++) {
                if (v1[i] != cs.charAt(i)) {
                    return false;
                }
            }
            return true;
        }

這裏兩個方法重載,傳類型不同,CharSequence類型和StringBuffer類型

而實際操做起做用的是contentEquals(CharSequence cs)方法

而在這個方法中

if (cs instanceof AbstractStringBuilder) {
                     if (cs instanceof StringBuffer) {
                          synchronized(cs) {
                            return nonSyncContentEquals((AbstractStringBuilder)cs);
                       }
            } else {
                 return nonSyncContentEquals((AbstractStringBuilder)cs);
          }
    }

    能夠看出來,當咱們傳入的參數 cs 是AbstractStringBuilder的實例的時候 執行if內部的方法而且 若是是StringBuffer的實例,加同步鎖,而在方法nonSyncContentEquals中

    

private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
                    char v1[] = value;
                    char v2[] = sb.getValue();
                    int n = v1.length;
                    if (n != sb.length()) {
                        return false;
                    }
                    for (int i = 0; i < n; i++) {
                        if (v1[i] != v2[i]) {
                            return false;
                        }
                    }
                    return true;
                }

        這個方法中吧,感受到無語,首先先獲取參數的對應字符數組的長度,而後比較字符數組的長度,若是長度不相同,那麼直接返回false,也就是這兩個字符串的序列號確定不同,
                若是長度同樣,再一循環比較每個字符

                若是不知足if中的判斷語句後,在向下執行,

// Argument is a String
                if (cs instanceof String) {
                    return equals(cs);
                }

                若是傳參是String的實例的時候 
                則調用了String的equals方法進行比較

                若是不知足上面的條件,再向下執行

// Argument is a generic CharSequence
                    char v1[] = value;
                    int n = v1.length;
                    if (n != cs.length()) {
                        return false;
                    }

                能夠看到這一步是直接比較 的字符參數的長度 ,若是長度不同,那麼其對應的序列號確定也就不同了
                若是不知足if條件

for (int i = 0; i < n; i++) {
                        if (v1[i] != cs.charAt(i)) {
                            return false;
                        }
                    }

                能夠看到繼續向下,則是比較每個字符,不相同返回false;
                若是不知足上述方法,則最後返回true

 

3.12 感受無力的 copyValueOf方法

(2016-9-22 18:43 更新)

String string = "4444";
        
        char[] charTest = new char[]{'d','r','w','q'};
        
        String newString = string.copyValueOf(charTest);

        構建結果盡然是: drwq 

        不忍心看源碼,可是還得看下,一看,更是無語

/**
         * Equivalent to {@link #valueOf(char[])}.
         *
         * @param   data   the character array.
         * @return  a {@code String} that contains the characters of the
         *          character array.
         */
        public static String copyValueOf(char data[]) {
            return new String(data);
        }

    擦,盡然是調用了String的一個構造來構造了一個新的 String 

3.13 判斷字符串是否以指定的字符或者字符串開頭

(2016-9-23 8:33 更新)

String string = "andThisIs4";
        //判斷是否以a開頭
        boolean flag = string.startsWith("a");
        //輸出結果爲 true

從源碼角度來看:

public boolean startsWith(String prefix) {
            return startsWith(prefix, 0);
        }

這裏調用其重載方法
        對於這個方法來講,傳入兩個參數 
        String string = "thisIsAndAnd";
        //判斷從二號索引位置 是不是以this開頭
        boolean flag = string.startsWith("this",2);
        //結果 爲 flase 

public boolean startsWith(String prefix, int toffset) {
            char ta[] = value;
            int to = toffset;
            char pa[] = prefix.value;
            int po = 0;
            int pc = prefix.value.length;
            // Note: toffset might be near -1>>>1.
            if ((toffset < 0) || (toffset > value.length - pc)) {
                return false;
            }
            while (--pc >= 0) {
                if (ta[to++] != pa[po++]) {
                    return false;
                }
            }
            return true;
        }

從上到下 :
         //獲取 父String 對應的字符數組 
         char ta[] = value;
         //賦值 檢索父String的起始位置 
            int to = toffset; 
         //獲取 子String 對應的字符數組 
            char pa[] = prefix.value;
         //定義變量標識
            int po = 0;
         //獲取 子String 對應字符數組的長度
            int pc = prefix.value.length;
         //若是檢索 父String的位置 小於0 返回false
         //若是 
            if ((toffset < 0) || (toffset > value.length - pc)) {
                return false;
            }
          //循環比較
            while (--pc >= 0) {
                if (ta[to++] != pa[po++]) {
                    return false;
                }
            }
            return true

3.14 判斷字符串是否以指定的字符或者字符串結尾

(2016-9-23 8:33 更新)

String string = "andrThisIs4";
        //判斷是不是以 4 結尾
        boolean flag = string.endsWith("4");
        //結果 爲true

從源碼角度來看 

public boolean endsWith(String suffix) {
                return startsWith(suffix, value.length - suffix.value.length);
            }

不忍直視,它盡然調用了 startsWith方法來執行判斷

3.15 比較兩個字符串中的內容是否相同

(2016-9-23 8:33 更新)

String text1 = "ABCD";
        String text2 = "ABCE";
        //比較兩個字符串是否相等
        boolean flage = text1.equals(text2);

 text1 text2 分別指向兩個堆內存空間 
        當使用 text1 == text2 來判斷時,比較的是text1 與 text2這兩個變量 對應的引用地址是否相等,也就是說其指向的內存地址是否同樣 

        從源碼角度來看 equals方法

public boolean equals(Object anObject) {
             if (this == anObject) {
                    return true;
                }
            if (anObject instanceof String) {
                String anotherString = (String)anObject;
                int n = value.length;
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;
        }


        參數格式要求是 Object類型

        在這個方法中,其先比較的兩個變量指向的堆內存空間地址是不是同樣,若是同樣,那麼直接返回true ,
            當 String text1 = "ABC";在內存空間開闢空間,並賦值變量text1
            當定義 Sring text2 ="ABC";時,虛擬機會先去內存空間的常量區中尋找是否有對應內容的空間,若是有,那麼就直接指向那邊,在這裏,因建立的text1 內容與 text2的內容一致,因此在text2建立的時候,會將text2的引用指向直接指向text1指向的內存空間

        在接下來的一步中,若是傳入的比較參數是String實例,將其強轉爲String 類型
        而後經過循環體制 一一比較每個字符,

 

3.16 獲取String默認格式下的字節編碼

(2016-9-27 14:33 更新) 

String text1 = "ABCDEF";
        //獲取默認編碼下的字節數組 
        byte[] bytes = text1.getBytes();

從源碼角度來看:

public byte[] getBytes() {
            return StringCoding.encode(value, 0, value.length);
        }

 這裏間接使用了StringCoding類的靜態方法 encode方法

static byte[] encode(char[] ca, int off, int len) {
            String csn = Charset.defaultCharset().name();
            try {
                // use charset name encode() variant which provides caching.
                return encode(csn, ca, off, len);
            } catch (UnsupportedEncodingException x) {
                warnUnsupportedCharset(csn);
            }
            try {
                return encode("ISO-8859-1", ca, off, len);
            } catch (UnsupportedEncodingException x) {
                // If this code is hit during VM initialization, MessageUtils is
                // the only way we will be able to get any kind of error message.
                MessageUtils.err("ISO-8859-1 charset not available: "
                                 + x.toString());
                // If we can not find ISO-8859-1 (a required encoding) then things
                // are seriously wrong with the installation.
                System.exit(1);
                return null;
            }
        }

        在這個方法中首先經過 String csn = Charset.defaultCharset().name(); 獲取默認的編碼方式(「UTF-8」)
        而後調用其重載的方法來進行編譯 ,
        若是出現異常,則使用 ISO-8859-1 的編碼方式來進行解析,若是再出現異常,系統異常退出 

3.17 獲取String指定編碼格式下的字節數組 

(2016-9-27 14:33 更新) 

String text1 = "abef";
        byte[] bytes = null;
        
        try {
            bytes = text1.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    可見這個方法是獲取默認編碼格式下的字節數組方法的重載方法

     從源碼角度來看 

    

public byte[] getBytes(String charsetName)
            throws UnsupportedEncodingException {
                if (charsetName == null) throw new NullPointerException();
                return StringCoding.encode(charsetName, value, 0, value.length);
        }

    能夠看到這裏直接調用的是 StringCoding 的四個參數的重載方法(getBytes()方法調用的是三個參數的encode方法)

3.18 將String 中的部分字符複製到指定的字符數組中去

(2016-9-27 14:33 更新) 

String text1  ="ABCDEF";
        //目標字符數組 
        char[] chars = new char[10];
        //拷貝
        text1.getChars(0,text1.length(),chars,0);
        //參數一 參數二  拷貝String中字符的範圍
        //參數三  目標字符數組
        //參數四  目標字符數組中的存儲起始位置

從源碼角度來看 

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
            if (srcBegin < 0) {
                throw new StringIndexOutOfBoundsException(srcBegin);
            }
            if (srcEnd > value.length) {
                throw new StringIndexOutOfBoundsException(srcEnd);
            }
            if (srcBegin > srcEnd) {
                throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
            }
            System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
        }

其直接調用 System 的拷貝數組的方法將數據拷貝

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

對於System的arraycopy方法來講,其直接調用 JNI層方法實現

        在上述調用 System的arraycopy方法的時候 傳入參數                  參數一  (value)          父級String對應的字符數組                  參數二  (srcBegin)     父級String開始拷貝數據的起始位置                  參數三  (dst)             目標數組                 參數四  (dstBegin)     目標數組存儲數據的起始位置                  參數五  (srcEnd - srcBegin) 截取父String中字符的長度

相關文章
相關標籤/搜索