Java jdk string_java JDK源码9:String(下)

该方法用于返回指定字符在此字符串中最后一次出现处的索引,有多种方法参数。可传入 int 类型,也可传入 String 类型,另外还能传入开始位置。根据编码的不同分别用 Latin1 和 UTF16 两种方式处理。

public int lastIndexOf(intch) {return lastIndexOf(ch, length() - 1);

}public int lastIndexOf(int ch, intfromIndex) {return isLatin1() ?StringLatin1.lastIndexOf(value, ch, fromIndex)

: StringUTF16.lastIndexOf(value, ch, fromIndex);

}public intlastIndexOf(String str) {returnlastIndexOf(str, length());

}public int lastIndexOf(String str, intfromIndex) {returnlastIndexOf(value, coder(), length(), str, fromIndex);

}static int lastIndexOf(byte[] src, byte srcCoder, intsrcCount,

String tgtStr,intfromIndex) {byte[] tgt =tgtStr.value;byte tgtCoder =tgtStr.coder();int tgtCount =tgtStr.length();int rightIndex = srcCount -tgtCount;if (fromIndex >rightIndex) {

fromIndex=rightIndex;

}if (fromIndex < 0) {return -1;

}if (tgtCount == 0) {returnfromIndex;

}if (srcCoder ==tgtCoder) {return srcCoder ==LATIN1?StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex)

: StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex);

}if (srcCoder ==LATIN1) {return -1;

}returnStringUTF16.lastIndexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex);

}

Latin1 编码的逻辑为,

* 判断 int 值是否能转成 byte,方法是看右移8位是否为0,为0即说明除了低8位其他都为0。

* 通过Math.min(fromIndex, value.length - 1)取偏移值。

* 从偏移处开始往前遍历查找,找到即返回索引值。

* 找不到返回-1。

public static int lastIndexOf(final byte[] value, int ch, intfromIndex) {if (!canEncode(ch)) {return -1;

}int off = Math.min(fromIndex, value.length - 1);for (; off >= 0; off--) {if (value[off] == (byte)ch) {returnoff;

}

}return -1;

}

类似地,对于 UTF16 编码也做类似处理,但因为 unicode 包含了基本多语言平面(Basic Multilingual Plane,BMP)外,还存在补充平面。而传入的值为 int 类型(4字节),所以如果超出 BMP 平面,此时需要4个字节,分别用来保存 High-surrogate 和 Low-surrogate,此时就需要对比4个字节。

另外,如果查找子字符串则是从子字符串第一个字符开始匹配直到子字符串完全被匹配成功。

substring方法

该方法用于获取字符串的指定子字符串。有两个方法,一个是只传入开始索引,第二个是出传入开始索引和结束索引。逻辑通过源码已经很清晰了,先计算截取的子字符串长度,然后分别根据 Latin1 和 UTF16 两种方式生成新的 String 对象。

public String substring(intbeginIndex) {if (beginIndex < 0) {throw newStringIndexOutOfBoundsException(beginIndex);

}int subLen = length() -beginIndex;if (subLen < 0) {throw newStringIndexOutOfBoundsException(subLen);

}if (beginIndex == 0) {return this;

}return isLatin1() ?StringLatin1.newString(value, beginIndex, subLen)

: StringUTF16.newString(value, beginIndex, subLen);

}public String substring(int beginIndex, intendIndex) {int length =length();

checkBoundsBeginEnd(beginIndex, endIndex, length);int subLen = endIndex -beginIndex;if (beginIndex == 0 && endIndex ==length) {return this;

}return isLatin1() ?StringLatin1.newString(value, beginIndex, subLen)

: StringUTF16.newString(value, beginIndex, subLen);

}public static String newString(byte[] val, int index, intlen) {return new String(Arrays.copyOfRange(val, index, index +len),

LATIN1);

}public static String newString(byte[] val, int index, intlen) {if(String.COMPACT_STRINGS) {byte[] buf =compress(val, index, len);if (buf != null) {return newString(buf, LATIN1);

}

}int last = index +len;return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);

}

subSequence 方法

等同substring方法。

public CharSequence subSequence(int beginIndex, intendIndex) {return this.substring(beginIndex, endIndex);

}

concat方法

该方法用于将指定的字符串参数连接到字符串上。逻辑为,

* 获取待连接字符串长度,如果长度为0则直接返回本身。

* 两者编码如果相同,则直接通过System.arraycopy进行拷贝并返回新的 String 对象。

* 如果编码不同,则使用 UTF16 编码分别将二者的值拷贝到字节数组上并返回新的 String 对象。

publicString concat(String str) {int olen =str.length();if (olen == 0) {return this;

}if (coder() ==str.coder()) {byte[] val = this.value;byte[] oval =str.value;int len = val.length +oval.length;byte[] buf =Arrays.copyOf(val, len);

System.arraycopy(oval,0, buf, val.length, oval.length);return newString(buf, coder);

}int len =length();byte[] buf = StringUTF16.newBytesFor(len +olen);

getBytes(buf,0, UTF16);

str.getBytes(buf, len, UTF16);return newString(buf, UTF16);

}

replace方法

该方法用于替换字符串中指定的字符,分两种编码处理。

public String replace(char oldChar, charnewChar) {if (oldChar !=newChar) {

String ret= isLatin1() ?StringLatin1.replace(value, oldChar, newChar)

: StringUTF16.replace(value, oldChar, newChar);if (ret != null) {returnret;

}

}return this;

}

public static String replace(byte[] value, char oldChar, charnewChar) {if(canEncode(oldChar)) {int len =value.length;int i = -1;while (++i

}

}if (i

buf[j]=value[j];

}while (i

buf[i]= (c == (byte)oldChar) ? (byte)newChar : c;

i++;

}return newString(buf, LATIN1);

}else{byte[] buf =StringUTF16.newBytesFor(len);

inflate(value,0, buf, 0, i);while (i

StringUTF16.putChar(buf, i, (c== oldChar) ?newChar : c);

i++;

}return newString(buf, UTF16);

}

}

}return null;

}

publicString replace(CharSequence target, CharSequence replacement) {

String tgtStr=target.toString();

String replStr=replacement.toString();int j =indexOf(tgtStr);if (j < 0) {return this;

}int tgtLen =tgtStr.length();int tgtLen1 = Math.max(tgtLen, 1);int thisLen =length();int newLenHint = thisLen - tgtLen +replStr.length();if (newLenHint < 0) {throw newOutOfMemoryError();

}

StringBuilder sb= newStringBuilder(newLenHint);int i = 0;do{

sb.append(this, i, j).append(replStr);

i= j +tgtLen;

}while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);return sb.append(this, i, thisLen).toString();

}

replaceFirst和replaceAll

都是用正则去实现。

publicString replaceFirst(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceFirst(replacement);

}publicString replaceAll(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceAll(replacement);

}

split方法

该方式用于切分字符串,实现中首先会判断能不能不用正则引擎,如果可以则直接切分,否则采用正则引擎切分。

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

}public String[] split(String regex, intlimit) {char ch = 0;if (((regex.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 list = new ArrayList<>();while ((next = indexOf(ch, off)) != -1) {if (!limited || list.size() < limit - 1) {

list.add(substring(off, next));

off= next + 1;

}else{int last =length();

list.add(substring(off, last));

off=last;break;

}

}if (off == 0)return new String[]{this};if (!limited || list.size()

list.add(substring(off, length()));int resultSize =list.size();if (limit == 0) {while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {

resultSize--;

}

}

String[] result= newString[resultSize];return list.subList(0, resultSize).toArray(result);

}return Pattern.compile(regex).split(this, limit);

}

join方法

用某个分隔符将字符串数组连接起来。主要是通过 StringJoiner 类来实现。

public staticString join(CharSequence delimiter, CharSequence... elements) {

Objects.requireNonNull(delimiter);

Objects.requireNonNull(elements);

StringJoiner joiner= newStringJoiner(delimiter);for(CharSequence cs: elements) {

joiner.add(cs);

}returnjoiner.toString();

}public staticString join(CharSequence delimiter,

Iterable extends CharSequence>elements) {

Objects.requireNonNull(delimiter);

Objects.requireNonNull(elements);

StringJoiner joiner= newStringJoiner(delimiter);for(CharSequence cs: elements) {

joiner.add(cs);

}returnjoiner.toString();

}

StringJoiner 的 add 方法和 toString 方法如下。add 方法主要逻辑就是将每个字符串赋值到字符串数组,并且要将分隔符的长度累加起来。toString 方法主要是将字符串数组和分隔符连接起来并返回最终的字符串。

publicStringJoiner add(CharSequence newElement) {final String elt =String.valueOf(newElement);if (elts == null) {

elts= new String[8];

}else{if (size ==elts.length)

elts= Arrays.copyOf(elts, 2 *size);

len+=delimiter.length();

}

len+=elt.length();

elts[size++] =elt;return this;

}

publicString toString() {final String[] elts = this.elts;if (elts == null && emptyValue != null) {returnemptyValue;

}final int size = this.size;final int addLen = prefix.length() +suffix.length();if (addLen == 0) {

compactElts();return size == 0 ? "" : elts[0];

}final String delimiter = this.delimiter;final char[] chars = new char[len +addLen];int k = getChars(prefix, chars, 0);if (size > 0) {

k+= getChars(elts[0], chars, k);for (int i = 1; i < size; i++) {

k+=getChars(delimiter, chars, k);

k+=getChars(elts[i], chars, k);

}

}

k+=getChars(suffix, chars, k);returnjla.newStringUnsafe(chars);

}private voidcompactElts() {if (size > 1) {final char[] chars = new char[len];int i = 1, k = getChars(elts[0], chars, 0);do{

k+=getChars(delimiter, chars, k);

k+=getChars(elts[i], chars, k);

elts[i]= null;

}while (++i

size= 1;

elts[0] =jla.newStringUnsafe(chars);

}

}

toLowerCase方法

用于转成小写,需要分两种编码处理,下面看 Latin1 编码的处理逻辑,

publicString toLowerCase(Locale locale) {return isLatin1() ? StringLatin1.toLowerCase(this, value, locale)

: StringUTF16.toLowerCase(this, value, locale);

}publicString toLowerCase() {returntoLowerCase(Locale.getDefault());

}

遍历字节数组,分别转成 int 类型值,然后通过Character.toLowerCase检查是否全部为小写,如果是则直接返回字符串本身。

如果为(lang == "tr" || lang == "az" || lang == "lt")三者语言,则额外处理,因为不在 Latin1 编码中。

正常情况下先用System.arraycopy赋值一个新数组,然后通过遍历源数组,将一个个字符转成小写再赋给新数组。

根据新数组创建新 String 对象并返回。

public static String toLowerCase(String str, byte[] value, Locale locale) {if (locale == null) {throw newNullPointerException();

}intfirst;final int len =value.length;for (first = 0 ; first < len; first++) {int cp = value[first] & 0xff;if (cp !=Character.toLowerCase(cp)) {break;

}

}if (first ==len)returnstr;

String lang=locale.getLanguage();if (lang == "tr" || lang == "az" || lang == "lt") {return toLowerCaseEx(str, value, first, locale, true);

}byte[] result = new byte[len];

System.arraycopy(value,0, result, 0, first);for (int i = first; i < len; i++) {int cp = value[i] & 0xff;

cp=Character.toLowerCase(cp);if (!canEncode(cp)) {return toLowerCaseEx(str, value, first, locale, false);

}

result[i]= (byte)cp;

}return newString(result, LATIN1);

}

toUpperCase方法

用于将字符串成转成大写,实现逻辑与上面的转小写一样。

publicString toUpperCase(Locale locale) {return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)

: StringUTF16.toUpperCase(this, value, locale);

}publicString toUpperCase() {returntoUpperCase(Locale.getDefault());

}

trim方法

用于删除字符串的头尾空白符,分两种编码处理,以 Latin1 为例,

publicString trim() {

String ret= isLatin1() ?StringLatin1.trim(value)

: StringUTF16.trim(value);return ret == null ? this: ret;

}

获取字节数组长度。

遍历看需要在字符串开头跳过多少个字符,小于等于空格 ASCII 值的都算在内。

遍历看需要在字符串尾部跳过多少个字符。

根据开头结尾跳过的空格数来创建新的 String 对象。

public static String trim(byte[] value) {int len =value.length;int st = 0;while ((st < len) && ((value[st] & 0xff) <= ' ')) {

st++;

}while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {

len--;

}return ((st > 0) || (len < value.length)) ?newString(value, st, len- st) : null;

}

toString方法

直接返回 this。

publicString toString() {return this;

}

toCharArray方法

将字符串转成 char 数组,分两种编码处理,以 Latin1 为例,核心就在(char)(src[srcOff++] & 0xff)。

public char[] toCharArray() {return isLatin1() ?StringLatin1.toChars(value)

: StringUTF16.toChars(value);

}

public static char[] toChars(byte[] value) {char[] dst = new char[value.length];

inflate(value,0, dst, 0, value.length);returndst;

}public static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, intlen) {for (int i = 0; i < len; i++) {

dst[dstOff++] = (char)(src[srcOff++] & 0xff);

}

}

format方法

格式化字符串,通过 Formatter 来实现。

public staticString format(String format, Object... args) {return newFormatter().format(format, args).toString();

}public staticString format(Locale l, String format, Object... args) {return newFormatter(l).format(format, args).toString();

}

valueOf方法

用于将传入的对象转成 String 对象,可传入多种类型参数。

* Objet 时,为空则返回”null”字符串,否则obj.toString()。

* char 数组时,直接new 一个 String 对象。

* boolean 时,返回”true” 或 “false”字符串。

* char 时,优先尝试转成 Latin1 编码的 String 读,否则用 UTF16。

* int 时,Integer.toString(i)。

* long 时,Long.toString(l)。

* float 时,Float.toString(f)。

* double 时,Double.toString(d)。

public staticString valueOf(Object obj) {return (obj == null) ? "null": obj.toString();

}public static String valueOf(chardata[]) {return newString(data);

}public static String valueOf(char data[], int offset, intcount) {return newString(data, offset, count);

}public static String valueOf(booleanb) {return b ? "true" : "false";

}public static String valueOf(charc) {if (COMPACT_STRINGS &&StringLatin1.canEncode(c)) {return newString(StringLatin1.toBytes(c), LATIN1);

}return newString(StringUTF16.toBytes(c), UTF16);

}public static String valueOf(inti) {returnInteger.toString(i);

}public static String valueOf(longl) {returnLong.toString(l);

}public static String valueOf(floatf) {returnFloat.toString(f);

}public static String valueOf(doubled) {returnDouble.toString(d);

}

intern方法

一个 native 方法,具体实现可看前面的文章《深入谈谈String.intern()在JVM的实现》

public native String intern();

getBytes方法

用于复制指定的字节数组,主要是通过System.arraycopy来实现。但如果目标数组为 UTF16 编码,则需将高位和低位都赋值到字节数组。

void getBytes(byte dst[], int dstBegin, bytecoder) {if (coder() ==coder) {

System.arraycopy(value,0, dst, dstBegin <

}else{

StringLatin1.inflate(value,0, dst, dstBegin, value.length);

}

}public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, intlen) {

checkBoundsOffCount(dstOff, len, dst);for (int i = 0; i < len; i++) {

putChar(dst, dstOff++, src[srcOff++] & 0xff);

}

}static void putChar(byte[] val, int index, intc) {

index<<= 1;

val[index++] = (byte)(c >>HI_BYTE_SHIFT);

val[index]= (byte)(c >>LO_BYTE_SHIFT);

}

coder方法

获取字符串的编码,如果使用非紧凑布局则一定为 UTF16,否则可能为 Latin1 或 UTF16。

bytecoder() {return COMPACT_STRINGS ?coder : UTF16;

}

isLatin1方法

判断是否为 Latin1 编码。必须是紧凑布局且为 LATIN1 才属于 Latin1 编码。

private booleanisLatin1() {return COMPACT_STRINGS && coder ==LATIN1;

}

版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值