String.java源码解析之理解indexOf函数

文章摘要
1、indexOf(String str,int fromIndex).
这里容易造成一个误解,认为[结果返回位置]要从fromIndex开始算起。
对于作为参数传入的fromIndex,只是用来标示[源字符串]开始查找的起始位置,不对返回Index产生影响。
2、【规则一】当开始查找位置 大于等于 源字符串长度时,如果[查找字符串]为空,则:返回字符串的长度,否则返回-1.
3、【规则三】如果[查找字符串]为空,则返回fromIndex。
4、源码解析indexOf执行过程,及其亮点。


一、indexOf函数简介
indexOf是String.java中的一个方法,用于返回[目标字符串]在[源字符串]中的位置。

1、indexOf:返回特定子字符串第一次在源字符串中的位置。如果源字符中不存在目标字符,则返回-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函数使用说明
indexOf存在多个重载函数,例如:

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

无论indexOf函数的重载有多少,返回位置都是相对于字符串开头而言的。

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

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

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

2、indexOf(String str,int fromIndex)
从fromIndex开始搜索,返回str第一次出现在字符串中的位置

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

这里容易造成一个误解,认为[结果返回位置]要从fromIndex开始算起。
对于作为参数传入的fromIndex,只是用来标示[源字符串]开始查找的起始位置,不对返回Index产生影响。

三、indexOf使用规则
1、【规则一】当开始查找位置 大于等于 源字符串长度时,如果[查找字符串]为空,则:返回字符串的长度,否则返回-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、【规则二】如果fromIndex 小于 0,则从 0开始查找。
对于查找“#”首次出现的位置,fromIndex 传入0和负数,逻辑相同。

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

3、【规则三】如果[查找字符串]为空,则返回fromIndex。

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

4、【规则四】如果未匹配到目标字符串,返回位置,否则:返回-1。

四、源码解析indexOf方法:
亮点:
1、匹配首个目标字符。for循环中,嵌套while循环,从源字符串中,找到匹配目标字符的位置。
2、小范围匹配目标字符串。for循环中,嵌套for循环,在[目标字符串]长度范围内,遍历匹配,如果全部匹配,则方法return 结束。

3、函数解析
indexOf的函数算法,以字符串source=“ABCABCDEFGABCABC”,从中找到target=“CDEFG”字符串,初始查找位置=0。执行过程大体可以分为如下几个步骤(5、6是其中的亮点之一):

  • 1、遍历source字符串。
  • 2、如果开始查找位置 大于等于 source字符串的长度,则:target字符串为空,返回字符串的长度,否则返回-1;。
    此处:初始查找位置为0,不符合红色标记的条件。
  • 3、如果开始查找位置小于0,则开始查找位置等于0.
    此处:初始查找位置为0,不符合红色标记的条件。
  • 4、查找字符串长度为0,则返回初始查找位置,此位置大于等于0.
    此处:初始查找字符串target长度为5,不符合红色标记条件。
  • 5、根据target字符串第一个字符,定位source字符串,第一个匹配source的位置。
for (int i = sourceOffset + fromIndex; i <= max; i++) {
    if (source[i] != first) { 
        while (++i <= max && source[i] != first);
    }
}

此处:通过遍历source字符串,内部增加一个while循环,在source字符串中,找到target第一个字符‘C’,赋值给i。

  • 6、匹配搜索的字符串是连续的,故而,根据target计算出来目标字符串长度‘end’,从5中的‘i’开始匹配,如果能够顺利匹配到end则,搜索结束并返回。
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’之后,无法继续匹配剩余字符串,故而匹配失败。

  • 7、如果匹配失败,则继续 5、6步骤,直至source字符串遍历结束。

4、原码实现及其注释

   static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex) {
        //1、当开始查找位置 大于等于 源字符串长度时,如果[查找字符串]为空,则:
        //返回字符串的长度,否则返回-1.
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        //2、如果fromIndex 小于 0,则从 0开始查找。
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        //3、如果[查找字符串]为空,则返回fromIndex
        if (targetCount == 0) {
            return fromIndex;
        }
        //4、开始查找,从[查找字符串]中得到第一个字符,标记为first
        char first  = target[targetOffset];
        //4.1、计算[源字符串最大长度]
        int max = sourceOffset + (sourceCount - targetCount);
        //4.2、遍历查找
        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            //4.2.1、从[源字符串]中,查找到第一个匹配到[目标字符串]first的位置
            //for循环中,增加while循环
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }
            //4.2.2、如果在[源字符串]中,找到首个[目标字符串],
            //则匹配是否等于[目标字符串]
            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                //4.2.2.1、得到下一个要匹配的位置,标记为j
                int j = i + 1;
                //4.2.2.2、得到其余[目标字符串]的长度,标记为end
                int end = j + targetCount - 1;
                //4.2.2.3、遍历,其余[目标字符串],从k开始,
                //如果j不越界(小于end,表示:其余[目标字符串]的范围),
                //同时[源字符串]==[目标字符串],则
                //自增,继续查找匹配。j++、k++
                for (int k = targetOffset + 1; j < end && source[j] ==
                         target[k]; j++, k++);
                //4.2.2.4、如果j与end相等,则表示:
                //源字符串中匹配到目标字符串,匹配结束,返回i。
                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        //其余情况,返回-1.
        return -1;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容