この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:10:02

タイトル: ビット演算子
SEOタイトル: Java ビット演算子の完全ガイド(& | ^ ~ << >> >>> とフラグ管理)

この記事の要点
  • & AND| OR^ XOR~ NOT は 2 進ビット単位の演算
  • << 左シフト>> 算術右シフト(符号維持)、>>> 論理右シフト(0 補填、Java 独自)
  • 用途: フラグ管理 / 高速演算 / 暗号 / ハッシュ / ビットマップ
  • Integer.bitCount(x) で立っているビット数を高速計算
  • JavaScript はビット演算で32-bit 整数に変換される(数値内部は 64-bit float)

ビット演算子の基本

ビット演算子は整数値を 2 進数として扱い、ビット単位で計算します。Java の int は 32 bit、long は 64 bit です。

演算子名称結果
&AND0b1100 & 0b10100b1000 (8)
|OR0b1100 | 0b10100b1110 (14)
^XOR (排他的論理和)0b1100 ^ 0b10100b0110 (6)
~NOT (補数)~0b00000000_00001100 (16bit表記)全ビット反転
<<左シフト0b0011 << 20b1100 (12)
>>算術右シフト-8 >> 1-4 (符号維持)
>>>論理右シフト-8 >>> 12147483644 (0 補填)

Java サンプル

int a = 0b1100;   // 12
int b = 0b1010;   // 10

System.out.println(a & b);   // 0b1000  = 8
System.out.println(a | b);   // 0b1110  = 14
System.out.println(a ^ b);   // 0b0110  = 6
System.out.println(~a);      // 0xFFFF_FFF3 = -13 (2の補数)

System.out.println(a << 1);  // 0b11000  = 24
System.out.println(a >> 1);  // 0b00110  = 6
System.out.println(-8 >> 1);   // -4 (符号維持)
System.out.println(-8 >>> 1);  // 2147483644 (0 補填) ← Java 独自

// 注: byte/short の場合 int に昇格してから演算
byte b1 = (byte) 0b11110000;
int  r  = b1 & 0xFF;   // 240 (符号なし化の定番イディオム)

用途 1: フラグ管理(ビットマスク)

複数のオン/オフ状態を 1 つの int に詰め込む古典的なテクニック。権限・状態の集合表現に使われます。

public class Permission {
    public static final int READ    = 1 << 0;   // 0b0001 = 1
    public static final int WRITE   = 1 << 1;   // 0b0010 = 2
    public static final int EXECUTE = 1 << 2;   // 0b0100 = 4
    public static final int DELETE  = 1 << 3;   // 0b1000 = 8
}

// セット
int perms = Permission.READ | Permission.WRITE;  // 0b0011

// 追加
perms |= Permission.EXECUTE;                     // 0b0111

// 削除
perms &= ~Permission.WRITE;                      // 0b0101

// 確認
boolean canRead = (perms & Permission.READ) != 0;

// トグル(あれば消す、無ければ付ける)
perms ^= Permission.DELETE;

// 立っているビット数(権限数)
int count = Integer.bitCount(perms);

Java では java.util.EnumSet がこの実装になっており、列挙型のセットを高速に扱えます。

用途 2: 高速演算

// 2 倍 / 2 で割る (符号付き整数で正の値のみ)
int x = 13;
int doubled = x << 1;   // 26
int halved  = x >> 1;   // 6  (切り捨て)

// 4 倍
int quad = x << 2;       // 52

// 2 の n 乗判定
boolean isPow2 = (x & (x - 1)) == 0 && x > 0;

// x mod 2^n (2^n - 1 とのマスクは / より高速)
int mod16 = x & 0b1111;  // x % 16 と同じ

// 偶奇判定
boolean isOdd = (x & 1) == 1;

// 注: JIT 最適化で速度差は小さくなっているが、読みやすさのために
// 通常は * 2 や / 2 を使う。bit 演算は意図が明確なときのみ

用途 3: XOR の便利技

// 2 変数の値の交換(テンポラリなし)
int a = 5, b = 7;
a = a ^ b;
b = a ^ b;   // = a^b^b = a (=5)
a = a ^ b;   // = a^b^a = b (=7)
// → a=7, b=5  ※ 実用上は temp 変数の方が読みやすい

// 値が 0 か非 0 かのトグル
int flag = 0;
flag ^= 1;   // → 1
flag ^= 1;   // → 0

// 配列内で 1 つだけ重複していない値を探す
// (他は 2 個ずつあるとき、全部 XOR すると残るのが答え)
int onlyOne(int[] a) {
    int r = 0;
    for (int x : a) r ^= x;
    return r;
}

シフト演算の詳細

// <<: 左シフト(右に 0 補填、上位ビット破棄)
//   結果は 2 の n 乗倍。オーバーフロー注意
int big = 1 << 31;   // -2147483648 (符号ビットに入った)

// >>: 算術右シフト(符号維持、上位は元の符号ビットで補填)
//   負数を 2 で割るときも符号維持
-8 >> 1;   // -4
-1 >> 1;   // -1 (全ビット 1 のまま)

// >>>: 論理右シフト(上位を 0 で補填)★ Java/JS 独自
//   負数のビットパターンを符号なしに扱える
-1 >>> 1;  // 2147483647 (0x7FFFFFFF)

// シフト量は対象型のビット数で剰余
int x = 5;
x << 33;   // == x << (33 % 32) == x << 1

// long の場合は 64 で剰余
long y = 5L;
y << 65;   // == y << 1

Integer の便利メソッド

Integer.bitCount(0b1011);              // 3 (立っているビット数)
Integer.numberOfLeadingZeros(0b1000);  // 28 (先頭の 0 個数)
Integer.numberOfTrailingZeros(0b1000); // 3  (末尾の 0 個数)
Integer.highestOneBit(0b1011);         // 8  (最上位の 1 だけ残す)
Integer.lowestOneBit(0b1100);          // 4  (最下位の 1 だけ残す)
Integer.reverse(0b00000000_00000000_00000000_00000001);
// → 0b10000000_00000000_00000000_00000000 = Integer.MIN_VALUE
Integer.reverseBytes(0x12345678);      // 0x78563412
Integer.toBinaryString(13);            // "1101"
Integer.parseInt("1101", 2);           // 13

JavaScript のビット演算

JavaScript の数値は内部 64-bit 浮動小数点ですが、ビット演算では32-bit 符号付き整数に変換されます。これが落とし穴に。

// ✅ 32-bit 範囲なら期待通り
0b1100 & 0b1010;        // 8
0b1100 | 0b1010;        // 14

// ❌ 32-bit を超えるとビットが捨てられる
const big = 2 ** 35;    // 34359738368
big | 0;                // 0 (上位ビットが捨てられた)

// ✅ BigInt なら任意精度
const bigBI = 2n ** 35n;
bigBI | 0n;             // 34359738368n

// 浮動小数の整数化トリック
3.7 | 0;                // 3 (Math.trunc と同じ効果)
~~3.7;                  // 3 (~~ も同様)

PHP のビット演算

$a = 0b1100;
$b = 0b1010;
echo $a & $b;     // 8
echo $a | $b;     // 14
echo $a ^ $b;     // 6
echo ~$a;         // -13
echo $a << 1;     // 24
echo $a >> 1;     // 6

// PHP には >>> (論理右シフト) は無い
// 符号なしシフトをするには
echo (int)(($a & 0xFFFFFFFF) >> 1);

// 整数型の長さ
echo PHP_INT_SIZE * 8;   // 64 (64-bit OS) / 32 (古い 32-bit OS)

注意点

  • 論理演算子 && / ||ビット演算子 & / | は別物。boolean に対しては前者を使う(短絡評価)
  • ~ は単項演算子。2 の補数表現なので ~x == -x - 1
  • シフト量が型のビット数を超えると剰余される(Java の 1 << 32 は 0 ではなく 1)
  • byte / short のビット演算は int に昇格 → 符号拡張で予期せぬ負数に → & 0xFF で符号なし化
  • 負の数の %& は等価ではない(負の数のとき結果が違う)

FAQ

Q: x & 0xFF ってよく見るけど何?
A: 下位 8 bit だけを残し、上位を 0 にするマスク。byte を符号なしの 0〜255 で扱いたいときの定番。

Q: x >> 1x / 2 はどちらが速い?
A: 現代の JIT/CPU では差はほぼ無し。可読性で / 2 を推奨。負数で挙動差があるので注意(-1 >> 1 = -1-1 / 2 = 0)。

Q: ハッシュテーブルでビット演算が使われるのは?
A: 容量を 2 の n 乗にしてインデックスを hash & (n - 1) で求めるパターン。剰余より高速。Java HashMap が採用。