Javaのビット演算子とシフト演算子を初心者向けにかみ砕いて解説します。
最初はピンと来なくても大丈夫。ここは「数を2進数(0と1)で見ると何ができる?」という世界。例題で感覚をつかみましょう。
ビット演算って何?
- ざっくり: 整数を0と1の列(ビット)として扱い、その1つ1つを直接いじる計算のこと。
- どこで役立つ? フラグ管理(ON/OFFの記録)、高速な処理、暗号や画像処理、組み込みなど。
例えば、10進数の「13」は2進数だと「1101」。この「1101」の各桁に対して計算するのがビット演算です。
4つの基本ビット演算子
AND: &(両方1のときだけ1)
- 直感:共通してONのビットだけを残す「絞り込みフィルター」。
int a = 13; // 13 = 1101(2)
int b = 6; // 6 = 0110(2)
int c = a & b; // 結果 = 0100(2) = 4
System.out.println(c); // 4
Java- 使いどころ: 「このフラグが立っているか?」の判定(後述のフラグ管理で実例)。
OR: |(どちらかが1なら1)
- 直感:持っているONのビットを全部まとめてONにする。
int a = 13; // 1101
int b = 6; // 0110
int c = a | b; // 1111 = 15
System.out.println(c); // 15
JavaXOR: ^(片方だけ1なら1)
- 直感:「違うところだけONにする」ハイライト。
int a = 13; // 1101
int b = 6; // 0110
int c = a ^ b; // 1011 = 11
System.out.println(c); // 11
JavaNOT: ~(0と1を反転)
- 注意:Javaの
intは32ビット。全部のビットが反転するため、負の数になることが多い。
int a = 13; // ...00000000000000000000000000001101
int c = ~a; // ...11111111111111111111111111110010
System.out.println(c); // -14
Javaシフト演算子(ビットを左右にずらす)
左シフト: <<(左へずらす=だいたい2倍ずつ)
- 直感:小数点なしの「×2の高速化」。ただし桁あふれに注意。
int x = 21; // 21 = 10101
int y = x << 2; // 1010100 = 84
System.out.println(y); // 84
Java右シフト(符号あり): >>(右へずらす=だいたい半分ずつ)
- 直感:小数点なしの「÷2の高速化」。負の数のとき、左端の符号ビットが保たれる。
int x = 21; // 10101
int y = x >> 2; // 101 = 5
System.out.println(y); // 5
Javaint n = -92;
int r = n >> 2; // 符号を保って右シフト
System.out.println(r); // -23
Java右シフト(符号なし): >>>(左端は必ず0で埋める)
- 直感:負の数でも「空いた左側は0」。結果は大きな正の数になりやすい。
int n = -92;
int r = n >>> 2; // 左側を0で埋める
System.out.println(r); // 1073741791 のような大きな正の数
Java実用例1:フラグ管理(ON/OFFを1つの整数にまとめる)
複数の設定を「ビット」に詰め込む。例えば、通知設定を3つ持っているとする:
- 1ビット目:メール通知
- 2ビット目:プッシュ通知
- 3ビット目:SMS通知
// フラグの定義(1, 2, 4 と2倍ずつにしておく)
final int EMAIL = 1 << 0; // 0001
final int PUSH = 1 << 1; // 0010
final int SMS = 1 << 2; // 0100
int setting = 0; // 0000(全部OFF)
// ONにする(ORで足していく)
setting |= EMAIL; // 0001
setting |= PUSH; // 0011
// 立っているか確認(ANDでチェック)
boolean emailOn = (setting & EMAIL) != 0; // true
boolean smsOn = (setting & SMS) != 0; // false
// OFFにする(ANDとNOTの組み合わせ)
setting &= ~PUSH; // 0001(PUSHをOFF)
System.out.println(emailOn); // true
System.out.println(smsOn); // false
System.out.println(setting); // 1
Java- 覚え方
- ONにしたい→ OR(
|=) - 立っているか確認→ AND(
&) - OFFにしたい→ AND+NOT(
&= ~FLAG)
- ONにしたい→ OR(
実用例2:効率的な掛け算・割り算(2の冪が相手なら速い)
x << nはだいたいx * (2^n)x >> nはだいたいx / (2^n)(マイナスや端数は注意)
int price = 125; // 125円
int doublePrice = price << 1; // ×2 = 250
int quarter = price >> 2; // ÷4 = 31(小数は切り捨て)
System.out.println(doublePrice); // 250
System.out.println(quarter); // 31
Java実用例3:ビットマスクで一部だけ取り出す
下位4ビット(最後の4桁)だけ取り出したいとき:
int value = 0b1011_1101; // 189
int low4 = value & 0b1111; // 下位4ビットだけ → 1101 = 13
System.out.println(low4); // 13
Java- ポイント: 欲しい桁だけが1の「マスク」を用意してANDをかける。
よくあるつまずきと回避法
- 負の数のシフトが怖い
>>は符号を保つ、>>>は左を0で埋める。負の数のときにどちらを使うかを意識する。 ~で突然マイナスになる理由
32ビット全部反転するから。整数の内部表現(2の補数)でそうなる、と覚えておけばOK。- 掛け算・割り算の代わりにシフトを使うべき?
今のJavaでは通常の*や/でも十分速い。読みやすさを優先し、ビットが必要な場面だけ使う。
ミニ練習問題(答え付き)
- 次の式の結果は?
int a = 0b0101; int b = 0b0011; System.out.println(a & b);- 答え: 1(0101 & 0011 = 0001)
- 下位3ビットだけ取り出すコードを完成させて(
valueは任意のint)。int result = value & ________;- 答え:
0b0111(または7)
- PUSHフラグが立っているか判定する1行を書いて(
settingとPUSHは定義済み)。- 答え:
boolean on = (setting & PUSH) != 0;
- 答え:
int x = 40; x >> 3の結果は?- 答え: 5(40 ÷ 8 = 5)
まとめ
- AND/OR/XOR/NOTは「どのビットを残す・足す・違いを取る・反転する」ツール。
- << / >> / >>>は「ビットを動かして値を大きく/小さくする」ツール。
- 実務では「フラグ管理」「マスク」「高速な2の冪計算」によく使う。
もっと手を動かしたいなら、上のコードをそのままmainに入れてSystem.out.println()で結果を確認してみて。
