掌握与分析String类中常见的方法(openjdk-1.8):
- length、charAt、compareTo、compareToIgnoreCase、concat、contains
- startsWith、equals、indexOf、split
- tocharArray、toLowerCase、trim、valueOf
String底层:jdk1.8及以前使用是char[ ],1.9开始使用byte[ ];
ps:Java 9引入了Compact Strings来取代Java 6的Compressed Strings,它的实现更过彻底,完全使用byte[ ]来替代char[ ],同时新引入了一个字段coder来标识是LATIN1编码还是UTF16。
此文中的 value 为源码String类内部的char类型数组。
1、length()
用法:返回此字符串的长度;
public int length() {
return value.length;
}
2、charAt()
用法: 返回指定索引处的 char 值;
String类中的charAt():
public char charAt(int index) {
//value数组越过bound就会抛出异常
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
3、compareTo()
用法:
1,长度相同, 从第一位开始比较,如果相同返回0,如果不同则马上返回这两个字符的ascii值的差值;
2,如果俩串的前k个字符一样,则返回长度差, 直接返回长度差值(k = Math.min(len1, len2))。
String中的compareTo:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
返回ascii码差值
return c1 - c2;
}
k++;
}
如果俩串的前k个字符一样,则返回长度差
return len1 - len2;
}
4、compareToIgnoreCase()
用法:与compareTo用法一致,只是不考虑大小写
public int compareToIgnoreCase(String str) {
return CASE_INSENSITIVE_ORDER.compare(this, str);
}
底层是compare方法,其中调用了toUpperCase() 方法和toLowerCase()方法,这俩方法作用是实现大小写字母之间的转换。
因此compareToIgnoreCase()可以忽略大小写比较。
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
5、concat()
用法:将指定字符串连接到此字符串的结尾。
public String concat(String str) {
int otherLen = str.length();
//如果传入的字符串为空,直接返回当前字符串
if (otherLen == 0) {
return this;
}
int len = value.length;
//将value值复制到buf数组
char buf[] = Arrays.copyOf(value, len + otherLen);
//getChars() 方法将str复制到buf数组。底层调用System.arraycopy方法,是native方法。
str.getChars(buf, len);
return new String(buf, true);
}
}
6、contains()
用法:当此字符串包含指定的 char 值序列时,返回 true。
调用了String indexOf()方法
public boolean contains(CharSequence s) {
//int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
return indexOf(s.toString()) > -1;
}
7、startsWith()
用法:检测字符串是否以指定的前缀开始
//检测此字符串是否以指定的前缀开始。
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
// 检测此字符串从指定索引开始的子字符串是否以指定前缀开始。
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
//如果toffset小于0, 或toffset大于value.length - pc时返回false;
//这是因为当前String长达比传参字符串长度加上toffset还小的话,说明肯定不是以指定的前缀toffset开始。
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
//最多要比较pc个字符
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
8、equals
用法:重写了Object类中的方法,用于判断两个字符串是否相等(值比较)
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
//先判断anObject是不是String类的一个实例,不是则返回false
if (anObject instanceof String) {
类型转换,把anObject赋给anotherString
String anotherString = (String)anObject;
int n = value.length;
//两个String对象长度相等才可能一样,长度不等返回false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
遍历数组,比较 数组元素是否相同。如果有一个不同就返回false
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
9、indexOf()
用法:返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。找不到则返回 -1。
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;
}
10、split()
用法:根据匹配给定的正则表达式来拆分此字符串,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.value.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))
{
/* off为要找的子字符串的起始位置,next为其结束位置,即分隔符所在的位置。list用来储存子字符串。
而子字符串向list中添加的方式为list.add(substring(off, next));,也就是说,每次找到一个分隔符的时候,
就会向list中添加一次,而substring函数中,若起始位置与终止位置为相邻的话,只会添加一个"",也就是我们
在开始的实验中所看到的空的元素了。*/
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);
list.add(substring(off, value.length));
off = value.length;
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, value.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);
}
11、 tocharArray()
用法: 将字符串转换为字符数组。
就是将String中存储char序列(字符序列)的char数组(char[])直接复制给一个新的char数组,
底层调用System.arraycopy方法,由于类初始化顺序问题,无法使用Array.copyOf()。可能是因为在jvm启动时要用到toCharArray()方法,但Arrays类还没有初始化。
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
12、trim()
用法: 返回一个新字符串,删去前导空白和尾部空白
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
//遍历找出非空格的前导字符和尾部字符
while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
13、valueOf()
用法: 该方法是静态方法,重载方法有很多。
传参是String、int、long、float、double类型的调用了toString()方法
public static String valueOf(int i) {
return Integer.toString(i);
}
public static String valueOf(char c) {
char data[] = {c};
return new String(data, true);
}