Javaプログラミングにおいて、普段あまり意識することのない「ビット演算」。
「普通のアプリ開発では使わないのでは?」
このように思われがちですが、実はデータベースのフラグ管理や、パフォーマンスが要求される計算処理、画像処理などの分野では必須の技術です。
また、& と && の違いや、>> と >>> の違いなど、Java特有の細かいルールを理解していないと、思わぬバグを引き起こす原因にもなります。
この記事では、Javaにおけるビット演算子の基本的な使い方から、論理演算子との違い、そして実務で役立つ「ビットフラグ」の活用テクニックまで、サンプルコード付きで徹底解説します。
![]() 執筆者:マヒロ |
|
ビット演算とは?論理演算子との違い
ビット演算とは、整数データを「2進数(0と1の並び)」として扱い、ビット単位で計算を行う処理のことです。
コンピュータの内部構造に直接働きかけるため、非常に高速に処理できるという特徴があります。
ビット演算子と論理演算子の違い
Javaには & や | といった演算子がありますが、これらは条件式(if文)で使う && や || とは明確に異なります。
- 論理演算子 (
&&,||):boolean型(true/false)に対して使い、条件判定を行う。「短絡評価(左側だけで結果が決まれば右側を評価しない)」が行われる。 - ビット演算子 (
&,|,^など):整数型(int, longなど)に対して使い、ビットごとの演算を行う。短絡評価は行われない。
Javaで使えるビット演算子の一覧と使い方
Javaで利用可能な主要なビット演算子を紹介します。
それぞれの動作を理解するために、具体的なコードと実行結果を見ていきましょう。
1. AND演算子(&):論理積
両方のビットが「1」のときだけ「1」になります。
「特定のビットだけを取り出す(マスク処理)」際によく使われます。
public class BitwiseAndExample {
public static void main(String[] args) {
int a = 0b1010; // 10進数で10
int b = 0b1100; // 10進数で12
// 1010
// 1100
// ---- (&)
// 1000 -> 10進数で8
int result = a & b;
System.out.println("10 & 12 = " + result);
System.out.println("2進数表記: " + Integer.toBinaryString(result));
}
}
実行結果
10 & 12 = 8
2進数表記: 1000
0b という接頭辞をつけることで、2進数リテラルとして記述できます。
a と b の各ビットを比較し、両方が 1 の場所(最上位ビット)だけが 1 になっています。
2. OR演算子(|):論理和
どちらか一方でも「1」であれば「1」になります。
「特定のビットをONにする(フラグを立てる)」際によく使われます。
public class BitwiseOrExample {
public static void main(String[] args) {
int a = 0b1010; // 10
int b = 0b1100; // 12
// 1010
// 1100
// ---- (|)
// 1110 -> 10進数で14
int result = a | b;
System.out.println("10 | 12 = " + result);
System.out.println("2進数表記: " + Integer.toBinaryString(result));
}
}
実行結果
10 | 12 = 14
2進数表記: 1110
3. XOR演算子(^):排他的論理和
ビットが異なるときに「1」、同じときに「0」になります。
「特定ビットの反転」や「暗号化の基礎」として利用されます。
public class BitwiseXorExample {
public static void main(String[] args) {
int a = 0b1010; // 10
int b = 0b1100; // 12
// 1010
// 1100
// ---- (^)
// 0110 -> 10進数で6
int result = a ^ b;
System.out.println("10 ^ 12 = " + result);
System.out.println("2進数表記: " + Integer.toBinaryString(result));
}
}
実行結果
10 ^ 12 = 6
2進数表記: 110
4. NOT演算子(~):ビット反転
すべてのビットを反転させます(0を1に、1を0に)。これは単項演算子です。
public class BitwiseNotExample {
public static void main(String[] args) {
int a = 10; // 00...001010
// ビット反転(補数表現になるため、値は -11 になる)
int result = ~a;
System.out.println("~10 = " + result);
// 上位ビットも含めて表示される
System.out.println("2進数表記: " + Integer.toBinaryString(result));
}
}
実行結果
~10 = -11
2進数表記: 11111111111111111111111111110101
5. シフト演算子(<<, >>, >>>)
ビット列を左右にずらす演算です。掛け算や割り算の高速化に使われます。
- 左シフト (
<<): ビットを左にずらし、右側の空きを0で埋めます。2倍、4倍、8倍…にするのと同じ効果があります。 - 右シフト (
>>): ビットを右にずらします。符号付きシフトとも呼ばれ、最上位ビット(符号ビット)の値で空きを埋めます(正なら0、負なら1)。 - 符号なし右シフト (
>>>): ビットを右にずらしますが、符号を無視して必ず0で埋めます。Java特有の演算子です。
public class ShiftExample {
public static void main(String[] args) {
int a = 8; // 00...01000
int b = -8; // 11...11000 (2の補数)
System.out.println("8 << 1 = " + (a << 1)); // 16 (2倍)
System.out.println("8 >> 1 = " + (a >> 1)); // 4 (1/2倍)
System.out.println("-8 >> 1 = " + (b >> 1)); // -4 (符号維持)
System.out.println("-8 >>> 1 = " + (b >>> 1)); // 巨大な正の整数 (0埋めされるため)
}
}
実行結果
8 << 1 = 16
8 >> 1 = 4
-8 >> 1 = -4
-8 >>> 1 = 2147483644
ビット演算の実践的な使い道:ビットフラグ
ビット演算が最も輝くのは、複数の状態(ステータス)を1つの整数変数で管理する「ビットフラグ」です。
例えば、「読取可」「書込可」「実行可」といった権限管理を考えてみましょう。
フラグ管理のサンプルコード
public class BitFlagExample {
// 各フラグを2のべき乗で定義する
public static final int READ = 1; // 0001
public static final int WRITE = 2; // 0010
public static final int EXECUTE = 4; // 0100
public static final int DELETE = 8; // 1000
public static void main(String[] args) {
// 権限の設定:READ と WRITE を付与
// OR演算子 (|) でフラグをまとめる
int permission = READ | WRITE; // 0001 | 0010 = 0011 (3)
System.out.println("現在の権限値: " + permission);
// 権限の確認:READ 権限を持っているか?
// AND演算子 (&) で特定のビットが立っているか確認
if ((permission & READ) != 0) {
System.out.println("読取権限があります。");
}
// 権限の確認:EXECUTE 権限を持っているか?
if ((permission & EXECUTE) != 0) {
System.out.println("実行権限があります。");
} else {
System.out.println("実行権限はありません。");
}
// 権限の追加:EXECUTE を追加
permission |= EXECUTE; // 0011 | 0100 = 0111 (7)
System.out.println("実行権限を追加しました。権限値: " + permission);
// 権限の削除:WRITE を削除
// NOT (~) と AND (&) を組み合わせる
permission &= ~WRITE; // 0111 & 1101 = 0101 (5)
System.out.println("書込権限を削除しました。権限値: " + permission);
}
}
実行結果
現在の権限値: 3
読取権限があります。
実行権限はありません。
実行権限を追加しました。権限値: 7
書込権限を削除しました。権限値: 5
コードの解説
|で合成: 複数のフラグを足し合わせます。&で判定: 特定のフラグが含まれているかチェックします。(val & FLAG) != 0は定型句です。&= ~で削除: 特定のビットだけを0(OFF)にします。
この手法を使うと、データベースのカラムを節約できたり、引数の数を減らしたりすることができます。
高速化テクニック!偶数・奇数の判定
ビット演算を使うと、偶数か奇数かの判定を高速に行うことができます。
通常は num % 2 != 0 と書きますが、ビット演算では (num & 1) == 1 と書けます。
int num = 5;
if ((num & 1) == 1) {
System.out.println("奇数です");
} else {
System.out.println("偶数です");
}
最下位ビットが 1 なら奇数、0 なら偶数という性質を利用しています。
大量のデータを処理するループ内などで、わずかながらパフォーマンス向上が期待できます。
Javaのスキルを活かして年収を上げる方法
以上、Javaにおけるビット演算子の使い方について解説してきました。
なお、Javaのスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。
Javaエンジニアの需要は非常に高いため、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。
なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。
併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなJavaの案件を探しやすくなります。
転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。



