字符串compareTo源码解析
这个方法的源码需要参考三个java文件,即String.java ,StringUTF16.java ,StringLatin1.java
JDK9之后由byte类型的数组来存储String,维护了一个属性coder,它是一个编码格式的标识,使用LATIN1还是UTF-16,这个是在String生成的时候自动的,如果字符串中都是能用LATIN1就能表示的就是0,否则就是UTF-16.
private final byte[] value;
private final byte coder;
String类的compareTo方法如下:
public int compareTo(String anotherString) {
byte v1[] = value;
byte v2[] = anotherString.value;
byte coder = coder();//返回字符串编码方式LATIN1或者UTF16
if (coder == anotherString.coder()) {
return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
: StringUTF16.compareTo(v1, v2);
}
return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
: StringUTF16.compareToLatin1(v1, v2);
}
编码方式不同,则比较的方法也不同。LATIN1的编码是一个字节存储一个字符,UTF16是两个字节存储一个字符。
编码方式都是LATIN1
在字符串compareTo的时候,如果两个字符串的编码方式都是LATIN1,则调用StringLatin1.compareTo(v1, v2)
(v1, v2是两个字节数组)
public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
int lim = Math.min(len1, len2);//在较短的长度范围内比较
for (int k = 0; k < lim; k++) {
if (value[k] != other[k]) {
return getChar(value, k) - getChar(other, k);//执行到return的时候,循环自动退出。
}
}
return len1 - len2;//如果前几位都相同,则会返回长度差
}
getChar函数返回的是什么值呢?
public static char getChar(byte[] val, int index) {
return (char)(val[index] & 0xff);//保证val[index]低8位不变,高位补0,是为了保值。
}
可以看出getChar最后输出的是一个字符。
而getChar函数相减返回两个字符ascii十进制表示的差值。
ascii值表如下:https://blog.csdn.net/weixin_44893585/article/details/103068265
编码方式都是UTF-16
编码方式都是UTF-16,则调用StringUTF16.compareTo(v1, v2)
与StringLatin1.compareTo的方法是一样的,只是getChar不一样
static char getChar(byte[] val, int index) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;//首先左移一位,索引x2,因为两个字节代表一个字符
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
如果是UTF-16编码,取一个char,把index乘以2,再把连续的两个字节拼在一起就行了,这里因为是两个字节拼成char,涉及到了大小端问题,所以做了特殊处理。
static final int HI_BYTE_SHIFT;
static final int LO_BYTE_SHIFT;
static {
if (isBigEndian()) {//如果是大端
HI_BYTE_SHIFT = 8;
LO_BYTE_SHIFT = 0;
} else {//如果是小端
HI_BYTE_SHIFT = 0;
LO_BYTE_SHIFT = 8;
}
}
private static native boolean isBigEndian();
(native关键字表示这个方法不是java实现的,那么就不是原生态方法,也就不会存在这个文件中。开发java语言的时候用到,native关键字是与c++联合开发的时候,使java控制底层的,比如内存。)
字符串的编码方式不同
如果字符串的编码方式不同,调用不同编码类型字符串的比较方法
Latin1和UTF16相比
StringLatin1.compareToUTF16(v1, v2)
private static int compareToUTF16Values(byte[] value, byte[] other, int len1, int len2) {
int lim = Math.min(len1, len2);
for (int k = 0; k < lim; k++) {
char c1 = getChar(value, k);
char c2 = StringUTF16.getChar(other, k);
if (c1 != c2) {
return c1 - c2;//如果不相等,则返回ASCII字符对应的十进制的差值
}
}
return len1 - len2;//如果前几位都相等,则两个字符串长度之差。
}
UTF16和Latin1相比
StringUTF16.compareToLatin1(v1, v2)
这个函数的返回值是:-StringLatin1.compareToUTF16(other, value, len2, len1)
即StringLatin1.compareToUTF16的返回值取反