字符串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的返回值取反