在本文中,我們來看看如何使用位操作符實作低級别的位掩碼。我們将看到我們如何将一個單一的int變量作為一個單獨的資料容器。
位掩碼
位掩碼允許我們在一個數字變量中存儲多個值。我們不再把這個變量看作一個整數,而是把它的每一個比特當作一個獨立的值。
因為一個比特可以等于 0 或 1,我們也可以把它看成是 false 或 true 。我們也可以把一組比特切開,把它們當作一個較小的數字變量甚至是一個String。
舉個例子
假設我們有一個最小的記憶體空間,并且需要在一個int變量中存儲所有關于使用者賬戶的資訊。前八位(來自32個可用位)将存儲boolean資訊,如 "該賬戶是否激活?"或 "該賬戶是否溢價?"
至于剩下的24位,我們将把它們轉換成三個字元,作為使用者的辨別符。
編碼
我們的使用者将有一個辨別符 "AAA",他将有一個活躍的進階賬戶(存儲在前兩個比特)。在二進制表示中,它将看起來像。
String stringRepresentation = "01000001010000010100000100000011";
使用内置的Integer#parseUnsignedInt方法,可以很容易地将其編碼為一個int變量。
int intRepresentation = Integer.parseUnsignedInt(stringRepresentation, 2);
assertEquals(intRepresentation, 1094795523);
解碼
這個過程也可以用 Integer#toBinaryString 方法來反轉。
String binaryString = Integer.toBinaryString(intRepresentation);
String stringRepresentation = padWithZeros(binaryString);
assertEquals(stringRepresentation, "01000001010000010100000100000011");
提取一個比特
第一比特
如果我們想檢查我們賬戶變量的第一位,我們隻需要使用順位 and 運算符和數字 1 作為掩碼。因為數字 1在二進制形式中隻有第一位被設定為1,其餘的都是0,它将從我們的變量中删除所有的位,隻留下第一個完整的位。
10000010100000101000001000000011
00000000000000000000000000000001
-------------------------------- &
00000000000000000000000000000001
然後我們需要檢查産生的值是否不等于零。
intRepresentation & 1 != 0
任意位置的位
如果我們想檢查其他的位,我們需要建立一個适當的掩碼,這個掩碼需要在給定的位置上有一個位設定為1,其餘的設定為0。最簡單的方法是對我們已有的掩碼進行移位。
1 << (position - 1)
上面這行代碼的位置變量設定為3,将把我們的掩碼從 00000000000000000000000000000001 變成
00000000000000000000000000000100
是以,現在,比特方程将看起來像這樣。
10000010100000101000001000000011
00000000000000000000000000000100
-------------------------------- &
00000000000000000000000000000000
把所有這些放在一起,我們可以寫一個方法來提取給定位置上的單個比特。
private boolean extractValueAtPosition(int intRepresentation, int position) {
return ((intRepresentation) & (1 << (position - 1))) != 0;
}
為了達到同樣的效果,我們也可以将intRepresentation變量向相反方向移動,而不是改變掩碼。
提取多個比特
我們可以用類似的方法從一個整數中提取多個比特。讓我們提取我們的使用者帳戶變量的最後三個位元組,并将其轉換為一個字元串。首先,我們需要通過将變量向右移動來擺脫前八位的影響。
int lastThreeBites = intRepresentation >> 8;
String stringRepresentation = getStringRepresentation(lastThreeBites);
assertEquals(stringRepresentation, "00000000010000010100000101000001");
我們仍然有32位,因為int總是有32位。然而,現在我們隻對前24位感興趣,其餘的都是零,會很容易被忽略。我們建立的int變量可以很容易地用作整數ID,但是因為我們想有一個字元串ID,是以我們還有一個步驟要做。
我們将把二進制的字元串表示法分成8個字元的組,把它們解析成char變量,然後把它們連接配接成一個最終的String。
為了友善起見,我們還将忽略空位元組。
Arrays.stream(stringRepresentation.split("(?<=\\G.{8})"))
.filter(eightBits -> !eightBits.equals("00000000"))
.map(eightBits -> (char)Integer.parseInt(eightBits, 2))
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
.toString();
應用一個比特掩碼
我們也可以建立一個掩碼來同時檢查許多位,而不是提取和檢查單個位的值。我們想檢查我們的使用者是否有一個活躍的進階賬戶,是以他的變量的前兩個比特都設定為1。
我們可以用以前的方法分别檢查它們,但建立一個掩碼來選擇它們,會更快。
int user = Integer.parseUnsignedInt("00000000010000010100000101000001", 2);
int mask = Integer.parseUnsignedInt("00000000000000000000000000000011", 2);
int masked = user & mask;
因為我們的使用者有一個活躍的賬戶,但它不是進階賬戶,是以被屏蔽的值将隻有第一個比特被設定為1。
assertEquals(getStringRepresentation(masked), "00000000000000000000000000000001");
現在,我們可以輕松而廉價地斷言一個使用者是否符合我們的條件。
assertFalse((user & mask) == mask);
總結
在本教程中,我們學習了如何使用位運算符來建立位掩碼,并應用它