String.java源碼解析之理解indexOf函數(shù)

文章摘要
1、indexOf(String str,int fromIndex).
這里容易造成一個(gè)誤解,認(rèn)為[結(jié)果返回位置]要從fromIndex開(kāi)始算起。
對(duì)于作為參數(shù)傳入的fromIndex,只是用來(lái)標(biāo)示[源字符串]開(kāi)始查找的起始位置,不對(duì)返回Index產(chǎn)生影響。
2、【規(guī)則一】當(dāng)開(kāi)始查找位置 大于等于 源字符串長(zhǎng)度時(shí),如果[查找字符串]為空,則:返回字符串的長(zhǎng)度,否則返回-1.
3、【規(guī)則三】如果[查找字符串]為空,則返回fromIndex。
4、源碼解析indexOf執(zhí)行過(guò)程,及其亮點(diǎn)。


一、indexOf函數(shù)簡(jiǎn)介
indexOf是String.java中的一個(gè)方法,用于返回[目標(biāo)字符串]在[源字符串]中的位置。

1、indexOf:返回特定子字符串第一次在源字符串中的位置。如果源字符中不存在目標(biāo)字符,則返回-1。

2、JDK 源碼 API 介紹:

    /**
     * Returns the index within this string of the first occurrence of the
     * specified substring.
     *
     * <p>The returned index is the smallest value <i>k</i> for which:
     * <blockquote><pre>
     * this.startsWith(str, <i>k</i>)
     * </pre></blockquote>
     * If no such value of <i>k</i> exists, then {@code -1} is returned.
     *
     * @param   str   the substring to search for.
     * @return  the index of the first occurrence of the specified substring,
     *          or {@code -1} if there is no such occurrence.
     */
    public int indexOf(String str) {
        return indexOf(str, 0);
    }

    /**
     * Returns the index within this string of the first occurrence of the
     * specified substring, starting at the specified index.
     * @param   str         the substring to search for.
     * @param   fromIndex   the index from which to start the search.
     */
    public int indexOf(String str, int fromIndex) {
        return indexOf(value, offset, count,
                       str.value, str.offset, str.count, fromIndex);
    }

二、indexOf函數(shù)使用說(shuō)明
indexOf存在多個(gè)重載函數(shù),例如:

int indexOf(String str)
int indexOf(String str,int fromIndex).

無(wú)論indexOf函數(shù)的重載有多少,返回位置都是相對(duì)于字符串開(kāi)頭而言的。

1、indexOf(String str)
返回str第一次出現(xiàn)在字符串中的位置

String s = "12345#aaa#bbb#67890";
//字符串長(zhǎng)度:19
System.out.println("字符串長(zhǎng)度:"+s.length());

//‘#’第一次出現(xiàn)的位置
int n = s.indexOf("#");
//第一次出現(xiàn)的#字符的位置:5
System.out.println("第一次出現(xiàn)的#字符的位置:"+n);

2、indexOf(String str,int fromIndex)
從fromIndex開(kāi)始搜索,返回str第一次出現(xiàn)在字符串中的位置

String s = "12345#aaa#bbb#67890";
//‘#’第一次出現(xiàn)的位置
int n = s.indexOf("#");//n = 5
int k = s.indexOf("#",n+1);
//第二次出現(xiàn)的#字符的位置:9
System.out.println("第二次出現(xiàn)的#字符的位置:"+k);

這里容易造成一個(gè)誤解,認(rèn)為[結(jié)果返回位置]要從fromIndex開(kāi)始算起。
對(duì)于作為參數(shù)傳入的fromIndex,只是用來(lái)標(biāo)示[源字符串]開(kāi)始查找的起始位置,不對(duì)返回Index產(chǎn)生影響。

三、indexOf使用規(guī)則
1、【規(guī)則一】當(dāng)開(kāi)始查找位置 大于等于 源字符串長(zhǎng)度時(shí),如果[查找字符串]為空,則:返回字符串的長(zhǎng)度,否則返回-1.

s = "12345#aaa#bbb#67890";
int m = s.indexOf("#",20);
//查找字符串為空,返回index:-1
System.out.println("查找字符串為空,返回index:"+m);

int l = s.indexOf("",20);
//查找字符串為空,返回index:19
System.out.println("查找字符串為空,返回index:"+l);

2、【規(guī)則二】如果fromIndex 小于 0,則從 0開(kāi)始查找。
對(duì)于查找“#”首次出現(xiàn)的位置,fromIndex 傳入0和負(fù)數(shù),邏輯相同。

s = "12345#aaa#bbb#67890";
int a = s.indexOf("#",0);
int b = s.indexOf("#",-2);
//fromIndex = 0 或者 fromIndex < 0,結(jié)果一致。a = 5,b = 5
System.out.println("fromIndex = 0 或者 fromIndex < 0,結(jié)果一致。a = " + a
        + ",b = "+b);

3、【規(guī)則三】如果[查找字符串]為空,則返回fromIndex。

s = "12345#aaa#bbb#67890";
int c = s.indexOf("",5);
//查找字符串為空,則返回fromIndex。返回::5
System.out.println("查找字符串為空,則返回fromIndex。返回::"+c);

4、【規(guī)則四】如果未匹配到目標(biāo)字符串,返回位置,否則:返回-1。

四、源碼解析indexOf方法:
亮點(diǎn):
1、匹配首個(gè)目標(biāo)字符。for循環(huán)中,嵌套while循環(huán),從源字符串中,找到匹配目標(biāo)字符的位置。
2、小范圍匹配目標(biāo)字符串。for循環(huán)中,嵌套for循環(huán),在[目標(biāo)字符串]長(zhǎng)度范圍內(nèi),遍歷匹配,如果全部匹配,則方法return 結(jié)束。

3、函數(shù)解析
indexOf的函數(shù)算法,以字符串source=“ABCABCDEFGABCABC”,從中找到target=“CDEFG”字符串,初始查找位置=0。執(zhí)行過(guò)程大體可以分為如下幾個(gè)步驟(5、6是其中的亮點(diǎn)之一):

  • 1、遍歷source字符串。
  • 2、如果開(kāi)始查找位置 大于等于 source字符串的長(zhǎng)度,則:target字符串為空,返回字符串的長(zhǎng)度,否則返回-1;。
    此處:初始查找位置為0,不符合紅色標(biāo)記的條件。
  • 3、如果開(kāi)始查找位置小于0,則開(kāi)始查找位置等于0.
    此處:初始查找位置為0,不符合紅色標(biāo)記的條件。
  • 4、查找字符串長(zhǎng)度為0,則返回初始查找位置,此位置大于等于0.
    此處:初始查找字符串target長(zhǎng)度為5,不符合紅色標(biāo)記條件。
  • 5、根據(jù)target字符串第一個(gè)字符,定位source字符串,第一個(gè)匹配source的位置。
for (int i = sourceOffset + fromIndex; i <= max; i++) {
    if (source[i] != first) { 
        while (++i <= max && source[i] != first);
    }
}

此處:通過(guò)遍歷source字符串,內(nèi)部增加一個(gè)while循環(huán),在source字符串中,找到target第一個(gè)字符‘C’,賦值給i。

  • 6、匹配搜索的字符串是連續(xù)的,故而,根據(jù)target計(jì)算出來(lái)目標(biāo)字符串長(zhǎng)度‘end’,從5中的‘i’開(kāi)始匹配,如果能夠順利匹配到end則,搜索結(jié)束并返回。
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;
 }

此處:字符‘C’之后,無(wú)法繼續(xù)匹配剩余字符串,故而匹配失敗。

  • 7、如果匹配失敗,則繼續(xù) 5、6步驟,直至source字符串遍歷結(jié)束。

4、原碼實(shí)現(xiàn)及其注釋

   static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex) {
        //1、當(dāng)開(kāi)始查找位置 大于等于 源字符串長(zhǎng)度時(shí),如果[查找字符串]為空,則:
        //返回字符串的長(zhǎng)度,否則返回-1.
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        //2、如果fromIndex 小于 0,則從 0開(kāi)始查找。
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        //3、如果[查找字符串]為空,則返回fromIndex
        if (targetCount == 0) {
            return fromIndex;
        }
        //4、開(kāi)始查找,從[查找字符串]中得到第一個(gè)字符,標(biāo)記為first
        char first  = target[targetOffset];
        //4.1、計(jì)算[源字符串最大長(zhǎng)度]
        int max = sourceOffset + (sourceCount - targetCount);
        //4.2、遍歷查找
        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            //4.2.1、從[源字符串]中,查找到第一個(gè)匹配到[目標(biāo)字符串]first的位置
            //for循環(huán)中,增加while循環(huán)
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }
            //4.2.2、如果在[源字符串]中,找到首個(gè)[目標(biāo)字符串],
            //則匹配是否等于[目標(biāo)字符串]
            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                //4.2.2.1、得到下一個(gè)要匹配的位置,標(biāo)記為j
                int j = i + 1;
                //4.2.2.2、得到其余[目標(biāo)字符串]的長(zhǎng)度,標(biāo)記為end
                int end = j + targetCount - 1;
                //4.2.2.3、遍歷,其余[目標(biāo)字符串],從k開(kāi)始,
                //如果j不越界(小于end,表示:其余[目標(biāo)字符串]的范圍),
                //同時(shí)[源字符串]==[目標(biāo)字符串],則
                //自增,繼續(xù)查找匹配。j++、k++
                for (int k = targetOffset + 1; j < end && source[j] ==
                         target[k]; j++, k++);
                //4.2.2.4、如果j與end相等,則表示:
                //源字符串中匹配到目標(biāo)字符串,匹配結(jié)束,返回i。
                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        //其余情況,返回-1.
        return -1;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容