java字符串compareTo源碼解析

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

結果是字符串compareTo函數返回的是ASCII字符對應的十進制的差值或者字符串長度之差。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章