Javaには、byte、short、int、long、charのビットを操作するための演算子があります。
<<を使うとビットを左にシフトでき、>>を使うと右にシフトできます。
// Shift two places to the left
0b0000_1011 << 2;
// # => 0b0010_1100
// Shift two places to the right
0b0000_1011 >> 2;
// # => 0b0000_0010
<<演算子は常に右側に0を挿入します。
ただし、>>は一番左のビットと同じビットを挿入します(数値が負なら1、正なら0)。
// Shift 2 places to the right preserves the sign
// This is a negative value, whose binary representation is
// 1000_0000_0000_0000_0000_0000_0010_0110
int value = -0x7FFFFFDA;
// Shift two places to the right, preserving the sign bit
value >> 2;
// # => 1110_0000_0000_0000_0000_0000_0000_1001
右にシフトするときに0を挿入したい場合は、代わりに>>>を使います。
// Shift two places to the right, inserting 0s on the left
value >>> 2;
// # => 0010_0000_0000_0000_0000_0000_0000_1001
ビット単位のAND演算子(&)は、2つの値を受け取り、各ビットに対してANDを実行します。
1つ目の値の各ビットを、2つ目の値の同じ位置にあるビットと比較します。
両方が1であれば、結果のビットは1になります。
そうでなければ、結果のビットは0になります。
0b0110_0101 & 0b0011_1100;
// # => 0b0010_0100
ビット単位のOR演算子(|)は、2つの値を受け取り、各ビットに対してORを実行します。
1つ目の値の各ビットを、2つ目の値の同じ位置にあるビットと比較します。
どちらかのビットが1であれば、結果のビットは1になります。
そうでなければ0になります。
0b0110_0101 | 0b0011_1100;
// # => 0b0111_1101
ビット単位のXOR演算子(^)は、2つの値に対してビット単位のXORを実行します。
ビット単位のAND演算子やビット単位のOR演算子と同様に、1つ目の値の各ビットを、2つ目の値の同じ位置にあるビットと比較します。
どちらか一方だけが1であれば、結果のビットは1になります。
そうでなければ0になります。
0b0110_0101 ^ 0b0011_1100;
// # => 0b0101_1001
~)最後に、ビット単位のNOT演算子(~)は各ビットを反転します。
これまでの演算子とは異なり、これは単項演算子であり、1つの値だけに作用します。
~0b0110_0101;
// # => 0b1001_1010
友達から、重要な秘密が書かれたメッセージが届きました。 他の人に簡単に読まれないように、このメッセージは一連のビット操作によって暗号化されています。 メッセージの復号を手助けするメソッドを書く必要があります。
メッセージを復号する最初のステップは、暗号化の過程で行ったシフトを元に戻すことです。ビットを右にシフトし直します。 このあとの復号のステップでは、左側から0が挿入されることを前提としています。
値とシフトする桁数を受け取り、シフトを実行するSecrets.shiftBackメソッドを実装しましょう。
Secrets.shiftBack(0b1001, 2);
# => 0b0010
次に、1に設定する必要があるビットがあります。
値とマスクを受け取り、値のビットを1に設定した結果を返すSecrets.setBitsメソッドを実装しましょう。
マスクのビットが1のところでは、値の対応するビットも1に設定します。
それ以外のビットは変更しません。
Secrets.setBits(0b0110, 0b0101);
# => 0b0111
暗号化の際に、いくつかのビットが反転しています。 メッセージを復号するには、それらを反転し直す必要があります。
値とマスクを受け取るSecrets.flipBitsメソッドを実装しましょう。
マスクは、値のどのビットを反転させるかを示します。
マスクのビットが1であれば、値の対応するビットを反転します。
それ以外のビットは変更しません。
Secrets.flipBits(0b1100, 0b0101);
# => 0b1001
最後に、復号すると必ず0になるビットもあります。
値とマスクを受け取るSecrets.clearBitsメソッドを実装しましょう。
マスクのビットが1のところでは、valueのビットを0に設定します。
それ以外のビットは変更しません。
Secrets.clearBits(0b0110, 0b0101);
# => 0b0010