1. 概要

Operators は、データと変数を操作するためにJava言語で使用されます。

このチュートリアルでは、ビット演算子とそれらがJavaでどのように機能するかについて説明します。

2. ビット演算子

ビット演算子は、入力値の2桁またはビットで機能します。これらを整数型( long、int、short、char、、およびbyte)に適用できます。

さまざまなビット演算子を調べる前に、まずそれらがどのように機能するかを理解しましょう。

ビット単位の演算子は、10進数に相当する2進数を処理し、指定された演算子に従ってビットごとに演算を実行します。

  • まず、オペランドがバイナリ表現に変換されます
  • 次に、演算子が各2進数に適用され、結果が計算されます
  • 最後に、結果は10進表現に変換されます。

例を挙げて理解しましょう。 2つの整数を取りましょう:

int value1 = 6;
int value2 = 5;

次に、これらの数値にビット単位のOR演算子を適用してみましょう。

int result = 6 | 5;

この操作を実行するには、最初に、これらの数値のバイナリ表現が計算されます。

Binary number of value1 = 0110
Binary number of value2 = 0101

次に、操作が各ビットに適用されます。 結果は新しい2進数を返します。

0110
0101
-----
0111

最後に、結果 0111 は、7に等しい10進数に変換されます。

result : 7

ビット単位の演算子は、ビット単位の論理演算子とビット単位のシフト演算子にさらに分類されます。 それぞれのタイプを見ていきましょう。

3. ビットごとの論理演算子

ビット単位の論理演算子は、AND(&)、OR(|)、XOR(^)、およびNOT(〜)です。

3.1. ビット単位または(|)

OR演算子は、2つの整数の各2桁を比較し、いずれかが1の場合は1を返します。

これは||に似ていますブール値で使用される論理演算子。 2つのブール値を比較すると、どちらかが trueの場合、結果は true になります。同様に、どちらかが1の場合、出力は1になります。

前のセクションでこの演算子の例を見ました:

@Test
public void givenTwoIntegers_whenOrOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 | value2;
    assertEquals(7, result);
}

この操作のバイナリ表現を見てみましょう。

0110
0101
-----
0111

ここで、ORを使用すると、0と0は0になり、少なくとも1との組み合わせは1になります。

3.2. ビット単位 (&)

AND演算子は、2つの整数の各2桁を比較し、両方が1の場合は1を返し、それ以外の場合は0を返します。

これは&&演算子に似ていますブール値値。 2つの値の場合ブール値それは真実 &&操作の結果は次のとおりです。 真実。

|の代わりに&演算子を使用することを除いて、上記と同じ例を使用してみましょう。 オペレーター:

@Test
public void givenTwoIntegers_whenAndOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 & value2;
    assertEquals(4, result);
}

この操作のバイナリ表現も見てみましょう。

0110
0101
-----
0100

0100は10進数で4であるため、結果は次のようになります。

result : 4

3.3. ビット単位のXOR(^)

XOR演算子は、2つの整数の各2桁を比較し、比較された両方のビットが異なる場合は1を返します。これは、両方の整数のビットが1または0の場合、結果は0になることを意味します。 それ以外の場合、結果は1になります。

@Test
public void givenTwoIntegers_whenXorOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int value2 = 5;
    int result = value1 ^ value2;
    assertEquals(3, result);
}

そして、バイナリ表現:

0110
0101
-----
0011

0011 は10進数で3であるため、結果は次のようになります。

result : 3

3.4. ビット単位の補数(〜)

Bitwise NotまたはComplement演算子は、単に入力値の各ビットの否定を意味します。 整数は1つだけで、!と同等です。 オペレーター。

この演算子は、整数の各2桁を変更します。つまり、すべての0が1になり、すべての1が0になります。 !! 演算子はboolean値に対しても同様に機能します。boolean値をtrueからfalseに逆にします。

ここで、10進数の補数を見つける方法を例を挙げて理解しましょう。

value1=6の補数を実行しましょう。

@Test
public void givenOneInteger_whenNotOperator_thenNewDecimalNumber() {
    int value1 = 6;
    int result = ~value1;
    assertEquals(-7, result);
}

バイナリの値は次のとおりです。

value1 = 0000 0110

補数演算子を適用すると、結果は次のようになります。

0000 0110 -> 1111 1001

これは、10進数の6の補数です。 また、最初の(左端の)ビットはバイナリで1であるため、格納されている数値の符号が負であることを意味します。

ここで、数値は2の補数として格納されるため、最初に2の補数を見つけてから、結果の2進数を10進数に変換する必要があります。

1111 1001 -> 0000 0110 + 1 -> 0000 0111

最後に、00000111は10進数で7です。 上記のように符号ビットは1であったため、結果の答えは次のようになります。

result : -7

3.5. ビット演算子テーブル

これまで見てきた演算子の結果を比較表にまとめてみましょう。

A	B	A|B	A&B	A^B	~A
0	0	0	0	0	1
1	0	1	0	1	0
0	1	1	0	1	1
1	1	1	1	0	0

4. ビットごとのシフト演算子

バイナリシフト演算子は、シフト演算子に基づいて、入力値のすべてのビットを左または右にシフトします。

これらの演算子の構文を見てみましょう。

value <operator> <number_of_times>

式の左側はシフトされる整数であり、式の右側はシフトする必要がある回数を示します。

ビット単位のシフト演算子は、ビット単位の左シフト演算子とビット単位の右シフト演算子にさらに分類されます。

4.1. 署名された左シフト[<<]

左シフト演算子は、オペランドの右側で指定された回数だけビットを左にシフトします。 左シフト後、右側の空きスペースは0で埋められます。

注意すべきもう1つの重要な点は、数値を1つシフトすることは、それを2で乗算することと同等です。または、一般に、数値をn桁左シフトすることは、2^nを乗算することと同等です。

値12を入力値としてみましょう。

次に、それを2桁左(12 << 2)に移動して、最終結果がどうなるかを確認します。

12に相当するバイナリは00001100です。 左に2桁シフトすると、結果は00110000になります。これは、10進数で48に相当します。

@Test
public void givenOnePositiveInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int leftShift = value << 2;
    assertEquals(48, leftShift);
}

これは、負の値に対しても同様に機能します。

@Test
public void givenOneNegativeInteger_whenLeftShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int leftShift = value << 2;
    assertEquals(-48, leftShift);
}

4.2. 署名された右シフト[>>]

右シフト演算子はすべてのビットを右にシフトします。入力番号に応じて、左側の空きスペースが埋められます。

  • 入力番号が負の場合、左端のビットが1の場合、空のスペースは1で埋められます。
  • 入力番号が正の場合、左端のビットは0であり、空のスペースは0で埋められます。

12を入力として使用して例を続けましょう。

次に、それを2桁右(12 >> 2)に移動して、最終結果がどうなるかを確認します。

入力された数値は正であるため、右に2桁シフトすると、結果は0011になり、小数点以下3桁になります。

@Test
public void givenOnePositiveInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int rightShift = value >> 2;
    assertEquals(3, rightShift);
}

また、負の値の場合:

@Test
public void givenOneNegativeInteger_whenSignedRightShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int rightShift = value >> 2;
    assertEquals(-3, rightShift);
}

4.3. 符号なし右シフト[>>>]

この演算子は、符号付き右シフト演算子と非常によく似ています。 唯一の違いは、数値が正か負かに関係なく、左側の空のスペースが0で埋められることです。したがって、結果は常に正の整数になります。

同じ値の12を右シフトしましょう。

@Test
public void givenOnePositiveInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
    int value = 12;
    int unsignedRightShift = value >>> 2;
    assertEquals(3, unsignedRightShift);
}

そして今、負の値:

@Test
public void givenOneNegativeInteger_whenUnsignedRightShiftOperator_thenNewDecimalNumber() {
    int value = -12;
    int unsignedRightShift = value >>> 2;
    assertEquals(1073741821, unsignedRightShift);
}

5. ビット演算子と論理演算子の違い

ここで説明したビット演算子と、より一般的に知られている論理演算子には、いくつかの違いがあります。

まず、論理演算子はブール式で機能し、ブール値( trueまたはfalse)、を返しますが、ビット演算子は機能します整数値( long、int、short、char、および byte )の2進数で、整数を返します。

また、論理演算子は常に最初の boolean 式を評価し、その結果と使用される演算子に応じて、2番目の式を評価する場合としない場合があります。 一方、ビット演算子は常に両方のオペランドを評価します。

最後に、論理演算子は複数の条件に基づいて決定を行う際に使用されますが、ビット単位の演算子はビットを処理し、ビットごとの演算を実行します。

6. ユースケース

ビット演算子の潜在的な使用例は次のとおりです。

  • データに添付されたヘッダーの個々のビットが重要な情報を示す通信スタック
  • 組み込みシステムでは、残りのビットを変更せずに、特定のレジスタの1つのビットだけを設定/クリア/トグルします
  • XOR演算子を使用して安全性の問題のためにデータを暗号化するには
  • データをある表現から別の表現に変換することによるデータ圧縮で、使用されるスペースの量を削減します

7. 結論

このチュートリアルでは、ビット演算子の種類と、それらが論理演算子とどのように異なるかについて学習しました。 また、それらの潜在的なユースケースもいくつか見ました。

この記事のすべてのコード例は、GitHubから入手できます。