共同学习Java源码--常用数据类型--String(十)

来源:互联网 发布:无损音乐剪切软件 编辑:程序博客网 时间:2024/05/21 23:22
    public int lastIndexOf(int ch) {
        return lastIndexOf(ch, value.length - 1);

    }

    public int lastIndexOf(int ch, int fromIndex) {
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            int i = Math.min(fromIndex, value.length - 1);
            for (; i >= 0; i--) {
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
            return lastIndexOfSupplementary(ch, fromIndex);
        }
    }

    private int lastIndexOfSupplementary(int ch, int fromIndex) {
        if (Character.isValidCodePoint(ch)) {
            final char[] value = this.value;
            char hi = Character.highSurrogate(ch);
            char lo = Character.lowSurrogate(ch);
            int i = Math.min(fromIndex, value.length - 2);
            for (; i >= 0; i--) {
                if (value[i] == hi && value[i + 1] == lo) {
                    return i;
                }
            }
        }
        return -1;
    }

这三个方法也要放在一起看。第一个方法调用第二个方法,第一个方法返回的是某代码点最后一次出现的下标。

第二个方法第一个参数是代码点的值,第二个参数是从哪个下标开始搜索,第一个方法传进来的参数是字符串长度减一,也就是从最后一个下标开始搜索。

第二个方法先判断代码点是否小于65536,如果大于等于这个65536,则调用第三个方法。如果小于65536则先用一个final的char数组变量引用本字符串的char数组value,然后取第二个参数和value的长度减一取最小值,然后进入for循环,循环次数就是刚才算出的那个最小值i,i逐一递减,比较每一个元素,如果发现相等就返回下标i。

第三个方法和上一篇的那个方法类似,此处不多赘述。只是循环的次数和第二个方法类似,都是参数和value长度减一之间的最小值,然后循环时这个最小值逐一递减判断。

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

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

    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            String target, int fromIndex) {
        return indexOf(source, sourceOffset, sourceCount,
                       target.value, 0, target.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;
    }

这四个方法放在一起看。第一个方法很常用,就是判断某个字符串是否存在于本字符串,并返回相应下标。第一个方法调用第二个方法,并且传入0作为第二个方法的第二个参数,也就是从0下标处开始搜索,这里的fromIndex是源字符串的开始下标。

第二个方法调用第四个方法,依次传入源字符串的value,源字符串开始下标0,源字符串的长度,要查询的字符串的value,要查询的字符串的开始下标,要查询的字符串的长度,源字符串开始搜索的下标。

第四个方法先判断源字符串的开始下标是否大于等于源字符串的长度,如果是,则返回一个三目运算符,这个三目运算符判断的是要查询的字符串长度是否为0,如果为0就返回源字符串的长度,否则返回-1,这是什么意思呢?如果你输入的源字符串起始下标(就是你从源字符串的那个下标开始搜索)大于源字符串的长度,且要查询的参数字符串长度为0就返回原字符串长度,要查询的参数字符串长度不为0,这时返回-1。

接下来判断如果输入的源字符串起始下标小于0,则会将起始下标修改为0,如果参数字符串长度为0,则返回源字符串起始下标。

然后创建一个临时变量first,赋值为参数字符串的起始下标处元素。再创建一个临时变量max,max的取值是源字符串开始下标加上两个字符串长度之差,然后进入for循环,i的起始值是源字符串起始下标和源字符串起始搜索下标之和,i的最大值为max,这怎么理解呢?举例子来说吧,你的字符串是"abcdefghijklmn",你要搜索的是"jkl",如果你调用的是第一个方法进行搜索,那么i就是0+0=0,max就是0+14-3=11,也就是从0开始到11位搜索下标的范围,如果你调用的是第二个方法并传入第二个参数1,那么i就是0+1=1,max就是0+14-3=11,也就是从下标1开始搜索一直搜索到11,如果你直接调用第四个静态方法,那么下标区间就很灵活了,还是举例子,如果第四个方法的第二个参数输入1,第四个参数输入2,最后一个参数输入3,那么first的值就是"jkl"的第2个下标l,i起始值就是1+3=4,max就是1+14-3=12,也就是从下标4开始,一直搜索到下标12,搜索的元素是"jkl"的l。

上面其实就是来回来去组合这俩字符串,其实没什么难的,循环里开始搜索参数字符串的指定下标的元素,没有搜到时,就进入嵌套的while循环为i自增然后再比较i下标的元素和first,直到两者相等,跳出while循环,当然前提是i不能大于等于max,这看上去很晦涩难懂,如果你是通过调用第一个方法进入第四个方法的,其实就是将参数字符串的第一个元素和源字符串的从0到两个字符串之差这段下标范围内的元素相比,就拿上面那个例子来说就是比"jkl"中的"j"和"abcdefghijklmn"中的前11个元素去比,也就是和"abcdefghijk"来比,为什么要和前11个比呢?因为源字符串长度是14,参数字符串长度是3,0+0到14-3=11的下标所在的元素都没找到,就没必要找第12 13下标的元素了。

在源字符串中找到和first元素一样的元素后,就开始搜索源字符串中和first元素一样的元素的后面。当然还要先判断i是否超过了max,如果超过了max则跳出for循环返回-1,如果没超过max,则创建一个j赋值为i+1,再创建一个end赋值为j加上参数字符串的长度减一,然后再进入一个没有循环体的for循环,这个for循环里循环变量是k,k就是参数字符串的起始下标加一,循环条件是j小于end且源字符串的第j个元素和参数字符串的第k个元素相等,然后j和k自增,如果循环没有提前结束也就是说找到了全部参数字符串那么就代表着j和end最后相等,就返回i-源字符串起始下标,否则进入下一次最外部的for循环。这也有点难懂,其实呢就是这么回事,还是拿上面那个例子举例,“abcdefghijklmn"中搜索"jkl",第一个"j"是源字符串下标为9的地方,i=9时跳出while循环,max是11,j是9+1=10,end是10+3-1=12,然后判断源字符串第j个元素是否和参数字符串第k个元素相等如果不等则退出循环体的for循环,这个的原理就是找到了"jkl"中的"j",要匹配"kl","kl"是从参数字符串的0+1个下标开始,是从源字符串的9+1个下标开始,从源字符串的10+3-1=12处结束,就是查找源字符串中和参数字符串第一个字符相等的字符的下标加一开始,判断参数字符串长度减一这么长的那一段子字符串是否和参数字符串剩下的部分相等,说白了就是找到了"j",“j”在第9位,那就看第10-11位是不是等于"kl",参数字符串匹配完“j”后,还剩下3-1个要匹配,所以在源字符串"j"的位置开始找3-1个。

这个挺绕的,不过确实写的挺巧妙的。

第三个方法就是把第四个方法的第4-6个参数封装成String,原理其实一样。

0 0
原创粉丝点击