有許多基本技術我們經常使用,甚至不假思索。那麼,如果你想一想,看看一些看似簡單的方法是如何實現的呢?我認為這將幫助我們更接近Java一步) 讓我們想像一種情況,我們需要提取某個字串中的某個字元。我們如何在 Java 中做到這一點?例如,透過調用
Java String charAt
. charAt()
我們將在今天的文章中討論該方法。
句法
char charAt(int index)
傳回指定索引處的 char 值。索引範圍從 0 到length()-1
。也就是說,char
序列的第一個值是 in index 0
,下一個值是 in ,index 1
等等,就像陣列索引的情況一樣。
例子
public static void main(String[] args) {
System.out.print("JavaRush".charAt(0));
System.out.print("JavaRush".charAt(1));
System.out.print("JavaRush".charAt(2));
System.out.print("JavaRush".charAt(3));
}
第一行採用第一個字符,第二行採用第二個字符,依此類推。由於這裡使用了 not println
, but print
,沒有換行,我們將在控制台得到以下輸出:
Java
如果char
給定索引表示為 Unicode,則該方法的結果java charAt()
將是表示該 Unicode 的字元:
System.out.println("J\u0061vaRush".charAt(1));
控制台輸出:
a
什麼是“引擎蓋下”
你問它是如何運作的?事實上,每個物件都String
包含一個數組byte
,其中包含給定字串的元素位元組:
private final byte[] value;
這是方法本身chatAt
:
public char charAt(int index) {
if (isLatin1()) {
return StringLatin1.charAt(value, index);
} else {
return StringUTF16.charAt(value, index);
}
}
isLatin1
- 一個標誌,指示我們的字串是否僅包含拉丁字元。這決定了接下來要呼叫哪個方法。
isLatin1 = true
如果字串僅包含拉丁字符,則呼叫靜態charAt
類別方法StringLatin1
:
public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
}
return (char)(value[index] & 0xff);
}
第一步是檢查傳入的索引是否大於或等於0,並且沒有超出內部位元組數組,如果不是,則拋出異常new StringIndexOutOfBoundsException(index)
。如果檢查通過,那麼我們需要的元素就會被獲取。最後我們看到:
&
將二進位運算擴展為byte
位元運算0xff
什麼都不做,但&
需要一個參數(char)
將資料從 ASCII 表轉換為char
isLatin1 = false
如果我們不僅僅有拉丁字符,則將使用該類別StringUTF16
並調用其靜態方法:
public static char charAt(byte[] value, int index) {
checkIndex(index, value);
return getChar(value, index);
}
這又調用:
public static void checkIndex(int off, byte[] val) {
String.checkIndex(off, length(val));
}
他委託靜態方法String
:
static void checkIndex(int index, int length) {
if (index < 0 || index >= length) {
throw new StringIndexOutOfBoundsException("index " + index +
", length " + length);
}
}
實際上,這裡會檢查索引是否有效:再次檢查它是正數還是零,以及它是否沒有超出數組的限制。但在類別中StringUTF16
的一個方法中,charAt
呼叫第二個方法會更有趣:
static char getChar(byte[] val, int index) {
assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
index <<= 1;
return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
((val[index] & 0xff) << LO_BYTE_SHIFT));
}
讓我們開始分析這裡實際發生的情況。這個方法開始的第一步是再次檢查索引的有效性。要了解接下來會發生什麼,您需要了解:當非拉丁字元進入陣列時value
,它由兩個位元組(兩個陣列單元)表示。如果我們有一個由兩個西里爾字母“av”組成的字串,那麼:
- 對於“a”,這是一對位元組 - 48 和 4;
- 對於 'in' - 50 和 4。
value
- {48, 4, 50, 4} 實際上,此方法適用於兩個數組單元value
。因此,下一步是index <<= 1;
直接取得數組中所需字元的第一個位元組的索引value
。現在假設我們有一個字串"абвг"
。則數值數組將如下所示:{48, 4, 49, 4, 50, 4, 51, 4}。我們要求字串的第三個元素,那麼二進位表示為 00000000 00000011。移位 1 後,我們得到 00000000 00000110,即index = 6
。要刷新您有關位元運算的知識,您可以閱讀這篇文章。我們也看到一些變數: HI_BYTE_SHIFT
在本例中為 0。 LO_BYTE_SHIFT
在本例中為 8。在此方法的最後一行:在此方法的最後一行:
- 從值數組中取出一個元素,按位移動
HI_BYTE_SHIFT
,即 0,同時增加index +1
。在字串 的範例中
"абвг"
,第六個位元組 - 51 - 將保持不變,但同時索引增加到 7。 - 此後,以相同的方式取得陣列的下一個元素並按位移位,但移位量為 LO_BYTE_SHIFT,即 8 位元。
如果我們有位元組 4,它具有二進位表示形式 - 00000000 00000100,那麼移位 8 位元後我們將得到 00000100 00000000。如果它是整數 - 1024。
- 接下來,對於這兩個值,進行操作
| (OR)
。如果我們有位元組 51 和 1024,在二進位表示中看起來像 00000000 00110011 和 00000100 000000000,那麼在運算之後
OR
我們將得到 00000100 00110011,這意味著十進制中的數字 1075。好吧,最後,數字1075被轉換為char類型,而在轉換int -> char時,使用了ASCII表,在其中,數字1075的下面,有字元'g'。
charAt()
。
GO TO FULL VERSION