1. ビット単位の左シフト

Java には 3 つのビット単位のシフト演算子もあります。本当に必要な場合は、数値のすべてのビットを数桁左または右に簡単にシフトできます。

数値のビットを左にシフトするには、ビット単位の左シフト演算子が必要です。これは次のように書かれています。

a << b

ここでa、 はビットがシフトされる数値であり、 は数値のビットを左にbシフトする回数を示す数値です。aこの操作中、右側に追加される下位ビットは 0 です。

例:

結果
0b00000011 << 1
0b00000110
0b00000011 << 2
0b00001100
0b00000011 << 5
0b01100000
0b00000011 << 20
0b001100000000000000000000

1 桁左にシフトすると、数値を 2 で乗算するのと同じ効果になります。

数値を 16 倍したいですか? 16 は 2 4と同じです。したがって、数字を4桁左にシフトします


2. ビット単位で右シフト

ビットを右にシフトすることもできます。これを行うには、ビット単位の右シフト演算子を使用します。これは次のように書かれています。

a >> b

ここで、aはビットがシフトされる数値、 は数値のビットを右にbシフトする回数です。a

例:

結果
0b11000011 >> 1
0b01100001
0b11000011 >> 2
0b00110000
0b11000011 >> 5
0b00000110
0b11000011 >> 20
0b00000000

1 桁右にシフトすると、数値を 2 で割ることと同じ効果が得られます。

この操作中、左側に追加される上位ビットは 0 ですが、常に!になるわけではありません。

重要!

符号付き数値の左端のビットは符号ビットと呼ばれます。数値が正の場合、それは です0。ただし、数値が負の場合、このビットは です1

数値のビットを右にシフトすると、通常、符号ビットの値もシフトし、数値の符号が失われます。したがって、負の数 (左端のビットが1) の場合、このビットは特別な扱いを受けます。数値のビットを右にシフトする場合、0左端のビットが の場合は左に a が追加され01左端のビットが の場合は左に a が追加されます1

しかし、上の例では、そのような結果は得られないようです。なぜ?整数リテラルはints であり、 実際には を意味するためです。つまり、左端のビットはゼロです。0b111111110b00000000000000000000000011111111

多くのプログラマは、この右シフト動作に不満を感じており、数値を常にゼロで埋めたいと考えています。そこで Java は別の右シフト演算子を追加しました。

これは次のように書かれています。

a >>> b

ここで、a はビットがシフトされる数値、 は数値のビットを右にb  シフトする回数です。aこの演算子は、数値の符号ビットの元の値に関係なく、常に左側にゼロを追加しますa



3. フラグの操作

プログラマは、ビット単位およびシフト演算に基づいて、フラグの操作というほぼまったく新しい研究分野を作成しました。

コンピューターのメモリが非常に少なかったとき、多くの情報を 1 つの数値に詰め込むことが非常に一般的でした。数値はビットの配列として扱われます。int は 32 ビット、long は 64 ビットです。

true特に論理 (または) 値を保存する必要がある場合は、このような数値に多くの情報を書き込むことができますfalse。シングルは64 個の要素で構成される配列longのようなものです。これらのビットはフラグbooleanと呼ばれ、次の操作を使用して操作されました。

  • フラグを設定する
    (特定のビットを と等しくします1)
  • リセットフラグ
    (特定のビットを と等しくします0)
  • チェックフラグ
    (特定のビットの値を確認する)

次に、ビットごとの演算子を使用してそれを行う方法を示します。

フラグの設定

特定のビットを に設定するには1、設定したいビットの数値と、そのビットのみが である特別に作成された数値の間でビットごとの OR 演算を実行する必要があります1

たとえば、数値があり0b00001010、5 番目のビットを に設定する必要があるとします1。その場合、次のことを行う必要があります。

0b00001010 | 0b00010000 = 0b00011010

5 番目のビットがすでに 1 に設定されている場合は、何も変更されません。

一般に、フラグを設定する操作は次のように記述できます。

a | (1 << b)

ここで、a はビットが に設定される番号です1b 設定するビットの位置です。ここで左シフト演算子を使用すると、どのビットを処理しているのかがすぐにわかるため、非常に便利です。

フラグをリセットする

他のビットに影響を与えずに特定のビットをリセットする (つまり に設定する) には、ビットをリセットする (つまり に設定する) 数値と特別に作成した数値の間で演算を0実行する必要があります。ここで、すべてのビットは次の値と等しくなります。リセットしたいビットを入力します。&01

たとえば、数値があり0b00001010、4 番目のビットを に設定する必要があるとします0。その場合、次のことを行う必要があります。

0b00001010 & 0b11110111 = 0b00000010

4 番目のビットがすでに 0 に設定されていた場合は、何も変化しません。

一般に、フラグをリセットする操作は次のように記述できます。

a & ~(1 << b)

ここで、a はビットが にリセットされる番号です0。そしてb クリアされるビットの位置です。

ゼロにしたいビット以外のすべてのビットが含まれる数値を取得するには、まず 1 b 位置を左に1シフトし、次にビットごとの演算子を使用して結果を反転します。NOT

フラグを確認する

特定のフラグを設定またはリセットすることに加えて、特定のフラグが設定されているかどうか、つまり特定のビットが に等しいかどうかだけを確認する必要がある場合があります1。これはビットごとに行うのが非常に簡単です&

たとえば、1数値の 4 番目のビットが に設定されているかどうかを確認する必要があるとします0b00001010。次に、これを行う必要があります。

if ( (0b00001010 & 0b00001000) == 0b00001000 )

一般に、フラグをチェックする操作は次のように記述できます。

(a & (1 << b)) == (1 << b)

ここで、 はa ビットがチェックされている番号です。そしてb チェックするビットの位置です。


4. 暗号化

ビット単位のXOR演算は、単純な暗号化のためにプログラマによってよく使用されます。一般に、このような暗号化は次のようになります。

result = number ^ password;

number 暗号化するデータはどこにあり、password はデータの「パスワード」として使用される特別な番号であり、 はresult 暗号化された番号です。

number == (number ^ password) ^ password;

ここで重要なことは、XOR演算子を数値に 2 回適用すると、「パスワード」に関係なく、元の数値が生成されるということです。

number から回復するにはencrypted result、次の操作を再度実行するだけです。

original number = result ^ password;

例:

class Solution
{
   public static int[] encrypt(int[] data, int password)
   {
     int[] result = new int[data.length];
     for (int i = 0; i <  data.length; i++)
       result[i] = data[i] ^ password;
     return result;
   }

   public static void main(String[] args)
   {
     int[] data =  {1, 3, 5, 7, 9, 11};
     int password = 199;

     // Encrypt the array of data
     int[] encrypted = encrypt(data, password);
     System.out.println(Arrays.toString(encrypted));

     // Decrypt the array of data
     int[] decrypted = encrypt(encrypted, password);
     System.out.println(Arrays.toString(decrypted));
   }
}