String源码详解三
indexOf
返回字符串对应数字编码字符的下标位置
indexOf(int ch)
public static void main(String[] args) {
//97对应的字符是a,返回hallo中第一个a的下标
int b = "hallo".indexOf(97);
System.out.println(b); //结果为1
}
indexOf(int ch, int fromIndex)
public static void main(String[] args) {
//97对应的字符为a,返回“halao”从下标2开始找字符为a的下标数
int b = "halao".indexOf(97,2);
System.out.println(b); //结果为3
}
indexOf(String str)
public static void main(String[] args) {
//a字符串在halao中第一个开始出现的下标
int a = "halao".indexOf("a"); // 1
//al字符串在halao中第一次出现的下标
int b = "halao".indexOf("al"); // 1
//ab字符串在halao第一处出现的下标,没有则返回-1
int c = "halao".indexOf("ab"); // -1
}
indexOf(String str, int fromIndex)
重载方法和上面的一样,有个参数即从这个参数位置开始找
indexOf(byte[] src, byte srcCoder, int srcCount, String tgtStr, int fromIndex)
列出其中一个核心方法的源码。
src 代表源字符串的字节数组
srcCoder 代表源字符串的编码方式
srcCount 代表源字符串的长度
tgtStr 代表子串
fromIndex 代表源字符串开始查找的下标位置
static int indexOf(byte[] src, byte srcCoder, int srcCount,
String tgtStr, int fromIndex) {
byte[] tgt = tgtStr.value;
byte tgtCoder = tgtStr.coder();
int tgtCount = tgtStr.length();
//如果开始查找的位置大于源字符串的长度,如果子串是空字符串就返回源字符串的长度
//否则返回-1
if (fromIndex >= srcCount) {
return (tgtCount == 0 ? srcCount : -1);
}
//下标开始处如果小于0则等于0
if (fromIndex < 0) {
fromIndex = 0;
}
//如果子串是空串,则下标处是几则返回几
if (tgtCount == 0) {
return fromIndex;
}
//如果子串的长度大于源字符串则返回-1
if (tgtCount > srcCount) {
return -1;
}
//根据不同的编码格式调用不同的方法来进行判断
if (srcCoder == tgtCoder) {
return srcCoder == LATIN1
? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex)
: StringUTF16.indexOf(src, srcCount, tgt, tgtCount, fromIndex);
}
// 到这里说明编码方式不一样,源字符串为LATIN1,说明子串为UTF16,那么源字符串里面
//肯定不会有子串,相当于英文字母数字组成的字符串中能出现汉字?
if (srcCoder == LATIN1) { // && tgtCoder == UTF16
return -1;
}
//源字符串为UTF16则再进行计算,相当于源字符串里面可能包含数字字母等。
//比如“你好nihao”是UTF16编码 "nihao"是Latin1编码,这样子就能进行比较
// srcCoder == UTF16 && tgtCoder == LATIN1) {
return StringUTF16.indexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex);
}
lastIndexOf
和IndexOf方法类似,只不过返回的是源字符串匹配子串的最后一个匹配的下标
public static void main(String[] args) {
int a = "halao".lastIndexOf("a"); // 1
System.out.println(a);// 3
}
和IndexOf()方法一样,也是有好几个重载方法,参数的含义也是一样的,就不多说了
lastIndexOf(int ch)
lastIndexOf(int ch, int fromIndex)
lastIndexOf(String str)
lastIndexOf(String str, int fromIndex)
static int lastIndexOf(byte[] src, byte srcCoder, int srcCount,
String tgtStr, int fromIndex) {
byte[] tgt = tgtStr.value;
byte tgtCoder = tgtStr.coder();
int tgtCount = tgtStr.length();
/*
* Check arguments; return immediately where possible. For
* consistency, don't check for null str.
*/
int rightIndex = srcCount - tgtCount;
if (fromIndex > rightIndex) {
fromIndex = rightIndex;
}
if (fromIndex < 0) {
return -1;
}
/* Empty string always matches. */
if (tgtCount == 0) {
return fromIndex;
}
if (srcCoder == tgtCoder) {
return srcCoder == LATIN1
? StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex)
: StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex);
}
if (srcCoder == LATIN1) { // && tgtCoder == UTF16
return -1;
}
// srcCoder == UTF16 && tgtCoder == LATIN1
return StringUTF16.lastIndexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex);
}
substring
这个方式是截取字符串的一个子串,有两个重载方法
substring(int beginIndex) //从指定下标处截取
System.out.println("abcd".substring(1));//bcd
从指定下标截取,截取到指定结尾下标位置
substring(int beginIndex, int endIndex)
System.out.println("abcd".substring(1,3));//bc
以其中一个源码说明
public String substring(int beginIndex) {
//开始下标小于0,直接抛异常
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
//开始下标大于字符串长度,也是直接抛异常
int subLen = length() - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
//开始下标等于0,返回这个字符串
if (beginIndex == 0) {
return this;
}
//根据不同的编码再生成一个新的字符串,
//所以截取这个方法是没有直接在原来的字符串上进行截取,因为String是不可变的。
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}
subSequence
看源码可知,这个方法就是调用subString,即作用也是截取指定的子串
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
concat
两个字符串进行拼接
public String 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 new String(buf, coder);
}
//如果两个字符串编码格式不一样,那么就按UTF16进行编码,合并成一个新字符串。
int len = length();
byte[] buf = StringUTF16.newBytesFor(len + olen);
getBytes(buf, 0, UTF16);
str.getBytes(buf, len, UTF16);
return new String(buf, UTF16);
}
replace replaceFirst replaceAll
这三个方法都是替换字符串,产生一个新的字符串
replace第一个参数是被替代的字符,第二个参数是要替换成哪个字符。只要符合的全部替换
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
: StringUTF16.replace(value, oldChar, newChar);
if (ret != null) {
return ret;
}
}
return this;
}
public String 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 new OutOfMemoryError();
}
StringBuilder sb = new StringBuilder(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 第一个参数可以是被替换的字符,也可以是正则表达式,从方法名可以看出来只替换第一个符合的。
public String replaceFirst(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
}
replaceAll
和replace不同的就是第一个参数可以为正则表达式。其余的一样,当两个方法的第一个参数都为要替换的字符串时,没有任何区别。
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
split
根据匹配给定的正则表达式来拆分字符串
regex 是正则表达式,或者字符串
limit 是要拆分的份数
有两个重载方法
split(String regex) 默认limit是0 ,代表能分几份就是几份
split(String regex, int limit)
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.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);
int last = length();
list.add(substring(off, last));
off = last;
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, 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);
}
join
join()方法是用指定字符来拼接多个字符串的,例如
String result = String.join("-",“A”,“B”,“C”,“D”);
结果:A-B-C-D
或者另一种用法:
String[] arr = {"-",“A”,“B”,“C”,“D”};
String result = String.join("-",arr);
结果:A-B-C-D
参数列表:
1、表示连接的符号
2、表示被连接的数组(也可以是集合),或者是要连接的多个字符串
有两个重载的方法如下是源码:
public static String join(CharSequence delimiter, CharSequence... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
// Number of elements not likely worth Arrays.stream overhead.
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
public static String join(CharSequence delimiter,
Iterable<? extends CharSequence> elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
StringJoiner joiner = new StringJoiner(delimiter);
for (CharSequence cs: elements) {
joiner.add(cs);
}
return joiner.toString();
}
toLowerCase
把字符串里面的英文单词都转换为小写,对中文没影响
String s = "asdadssA你好".toLowerCase();
//asdadssa你好
从源码可以看出里面还可以传个参数是Local,代表不同的区域。区域不一样进行转换可能会出现问题。
public String toLowerCase() {
return toLowerCase(Locale.getDefault());
}
toUpperCase
把字符串里面的英文单词都转换为大写,对中文没影响
String s = "asdadssA你好".toLowerCase();
//ASDADSSA你好
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
trim
去除字符串两边的空白字符,中间的空白字符无法去除
String trim = " a a a a ".trim();
//a a a a
public String trim() {
String ret = isLatin1() ? StringLatin1.trim(value)
: StringUTF16.trim(value);
return ret == null ? this : ret;
}
chars
这个方法返回字符串的int字符的流
public IntStream chars() {
return StreamSupport.intStream(
isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
: new StringUTF16.CharsSpliterator(value, Spliterator.IMMUTABLE),
false);
}
codePoints
这个方法同样返回字符串int字符的流,不同的在于代码点可能由于unicode中没有而返回的值不一致。
public IntStream codePoints() {
return StreamSupport.intStream(
isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
: new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE),
false);
}
toCharArray
这个方法很简单就是返回字符串的字符数组
public char[] toCharArray() {
return isLatin1() ? StringLatin1.toChars(value)
: StringUTF16.toChars(value);
}
format
这个方法实用性很强,是用来对字符串进行动态替换和格式化的,玩法多种多样,我一般用过的就是对日期进行处理,这里详细用法不做过多解释,可以参数format详细用法
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
valueOf
这个方法也很常用,用来将参数类型的数据转换为String类型
这里有狠多重载方法,可以将多种类型都转换为字符串。
copyValueOf
这个方法是重新拷贝一个新的字符串
传入的是字符数组,从源代码可以看出是以字符数组来重新生成一个字符串
参数offset是生成字符串的开始下标,count是生成的长度
char[] chars = "你好".toCharArray();
String s = "wo".copyValueOf(chars,0,1);
System.out.println(s); //结果为“你”
public static String copyValueOf(char data[]) {
return new String(data);
}
public static String copyValueOf(char data[], int offset, int count) {
return new String(data, offset, count);
}
intern
这个方法是一个本地方法,用来往常量池里面放引用的,这个底层关联的知识点很多,由于篇幅原因,就不展开说了,可以看看其他资料。
public native String intern();