Java超|ビット演算子

0と1の印を読み解けば、Javaの数値の裏側が見えてくる。
ビット演算子を知ると、数値を「コンピュータの目線」で操作できるようになる。

これまで学んできた演算子は、数値を計算したり、条件を判定したり、条件どうしを組み合わせたりするものが中心でした。

たとえば、+ や - は数値を計算するために使います。
== や > は、値を比較して true または false を得るために使います。
&& や || は、条件どうしを組み合わせるために使いました。

ここで少し視点を変えて、数値をもっと細かく見ていきます。

コンピュータの中では、数値は最終的に 0 と 1 の並びとして扱われます。
この 0 と 1 の1けた1けたをビットといいます。

ビット演算子は、数値を2進数として見たときの各ビットを対象にして操作する演算子です。

普段のJava学習では、10進数の 5 や 12 のような形で数値を見ることが多いです。
しかし、ビット演算子を学ぶと、5 は 00000101、12 は 00001100 のように、0 と 1 の並びとして見る感覚が身につきます。

ドラゴンボール風にたとえると、ビットは「戦士の装備札に刻まれた小さなエネルギー印」です。

1 は印が光っている状態。
0 は印が光っていない状態。

ビット演算子は、その印を1つずつ見比べて、共通している印だけを残したり、どちらかにある印を集めたり、違う印だけを取り出したりする技です。

ビット単位の論理演算子とは何か

ビット単位の論理演算子とは、整数を2進数で見たときの各けたごとに、0 と 1 を計算する演算子です。

たとえば、10進数の 5 は、2進数では次のように表せます。

00000101

10進数の 12 は、2進数では次のようになります。

00001100

ビット演算子は、この 0 と 1 を1けたずつ見比べます。

数値全体を普通に足したり引いたりするのではなく、各けたごとにルールを当てはめて、新しい 0 と 1 の並びを作ります。

10進数2進数の例
500000101
1200001100
400000100

ビット演算子を理解するときは、まず「数値を2進数として見る」という感覚が大切です。

ドラゴンボール風にたとえると、10進数の数値は「任務札の表に書かれた番号」です。
2進数は、その札の裏側に刻まれた小さなエネルギー印の並びです。

ビット演算子は、その裏側の印を1つずつ見て操作する技です。

ふつうの論理演算子との違い

ビット演算子を学ぶときに、特に気をつけたいのが、論理演算子との違いです。

これまでに学んだ &&、||、! は、true と false を対象にする演算子でした。

一方で、ビット演算子の &、|、^、~ は、整数型の値を2進数として見たときの各ビットを対象にします。

種類対象
論理演算子true と false&&、
ビット演算子整数型の各ビット&、

見た目が似ている記号もあります。

似ている記号違い
&& と &&& は条件、& はビットを扱う
|| と ||| は条件、| はビットを扱う
! と ~! は true / false を反転、~ は各ビットを反転

特に & と &&、| と || は混同しやすいです。

&& は、条件どうしを組み合わせます。
& は、整数のビットどうしを計算します。

ドラゴンボール風にたとえると、&& や || は「作戦判断そのもの」を組み合わせる札です。
一方、& や | は「任務札の裏に刻まれたエネルギー印」を1つずつ操作する技です。

まずは2進数の見方に慣れる

ビット演算子を理解するには、2進数の見方に慣れることが大切です。

10進数では、私たちは 5 や 12 のように数値を見ます。
しかし、コンピュータの中では、これらは 0 と 1 の並びとして扱われます。

たとえば、5 を2進数で表すと 101 です。
桁数をそろえて8ビットで書くと、次のようになります。

00000101

12 は、8ビットで書くと次のようになります。

00001100
10進数8ビットで見た2進数
500000101
1200001100
400000100
900001001
1300001101

左側に 0 を補って桁数をそろえると、ビットどうしを比べやすくなります。

ビット演算では、このように桁をそろえて、同じ位置のビットどうしを比較します。

ドラゴンボール風に言えば、複数の戦士の装備札を横に並べ、同じ位置のエネルギー印どうしを見比べるイメージです。

& は両方とも1なら1になる

ビット演算子の & は、左右のビットがどちらも 1 のときだけ、結果を 1 にします。

それ以外は 0 になります。

結果
000
010
100
111

これは、&& の「両方 true なら true」に少し似ています。

ただし、& は true / false ではなく、ビットの 0 / 1 を対象にしています。

5 & 12 の計算

10進数の 5 と 12 を2進数で並べます。

   5    00000101
& 12    00001100
----------------
        00000100

同じ位置のビットを1つずつ見比べます。

位置5 のビット12 のビット& の結果
右端100
右から2番目000
右から3番目111
右から4番目010

結果は 00000100 です。
これは10進数で 4 です。

つまり、次の結果は 4 になります。

5 & 12

& のイメージ

& は、両方に共通して 1 になっているビットだけを残す演算です。

2進数
500000101
1200001100
共通して1の場所00000100

ドラゴンボール風にたとえると、& は「2人の戦士が両方とも持っている装備印だけを残す技」です。

片方だけが持っている印は残りません。
両方に共通している印だけが、結果として残ります。

図:& は共通して1のビットだけを残す

この図が示していること

この図では、5 & 12 の計算を2進数で示しています。

5 は 00000101、12 は 00001100 です。
& は、同じ位置のビットが両方とも 1 のときだけ、結果を 1 にします。

この場合、共通して 1 になっているのは 00000100 の部分です。
そのため、結果は 00000100 となり、10進数では 4 になります。

ここから分かることは、& は数値全体をまとめて見るのではなく、各ビットを1つずつ比較しているということです。

| はどちらかが1なら1になる

次に、| を見ていきます。

| は、左右のビットのどちらか一方でも 1 なら、結果を 1 にする演算子です。

結果
000
011
101
111

これは、「1 があれば 1 にする」と考えると分かりやすいです。

5 | 12 の計算

5 と 12 を2進数で並べます。

   5    00000101
| 12    00001100
----------------
        00001101

結果は 00001101 です。
これは10進数で 13 です。

つまり、次の結果は 13 になります。

5 | 12

| のイメージ

| は、どちらかに 1 が立っているビットをまとめて残す演算です。

2進数
500000101
1200001100
1がある場所をまとめた結果00001101

& が共通部分を取り出す演算なら、| は1がある場所を合体させる演算です。

ドラゴンボール風にたとえると、| は「2人の戦士が持っている装備印を全部まとめる技」です。

どちらか一方が持っている印でも、結果に残ります。
2つの装備札の力を合体させるようなイメージです。

^ は異なるときに1になる

^ は、左右のビットが異なるときに 1 になり、同じときは 0 になる演算子です。

結果
000
011
101
110

少し独特ですが、ルールはシンプルです。

同じなら 0。
違えば 1。

5 ^ 12 の計算

5 と 12 を2進数で並べます。

   5    00000101
^ 12    00001100
----------------
        00001001

結果は 00001001 です。
これは10進数で 9 です。

つまり、次の結果は 9 になります。

5 ^ 12

^ のイメージ

^ は、左右で違うビットだけを 1 にします。

2進数
500000101
1200001100
違う場所だけ100001001

ドラゴンボール風にたとえると、^ は「2つの装備札を比べて、違っている印だけを光らせる技」です。

同じ印は消え、違う印だけが残ります。
共通点ではなく、差分を見つける技としてイメージすると分かりやすいです。

~ は0と1を反転する

~ は、これまでの &、|、^ と少し違います。

&、|、^ は、左右2つの値を比べる演算子です。
一方、~ は1つの値に対して使います。

~ は、各ビットを反転します。

元のビット結果
01
10

たとえば、8ビットで考えて、5 が次のように表されているとします。

00000101

これに ~ をつけると、0 と 1 がすべて反転します。

11111010

つまり、~ は、0 を 1 に、1 を 0 にひっくり返す演算子です。

ドラゴンボール風にたとえると、~ は「装備札の印をすべて反転する技」です。

光っていない印は光り、光っていた印は消えます。
表と裏を一気に入れ替えるような反転技です。

ビット演算子の一覧

ここまで見たビット演算子を整理すると、次のようになります。

演算子意味1ビットごとの考え方
&両方とも1なら1共通して1の場所だけ残す
|どちらかが1なら11がある場所をまとめる
^異なるときに1違いがある場所を1にする
~0と1を反転する各ビットをひっくり返す

最初は、記号だけを見ると少し覚えにくいかもしれません。

そんなときは、次のようにイメージすると分かりやすいです。

演算子イメージ
&共通部分を取り出す
|1がある場所を合体させる
^違う場所だけを見つける
~すべて反転する

図:&、|、^、~ の違いを比較する

この図が示していること

この図では、&、|、^、~ の違いをまとめて比較しています。

& は、両方とも 1 の場所だけを 1 にします。
| は、どちらか一方でも 1 の場所を 1 にします。
^ は、左右のビットが異なる場所だけを 1 にします。
~ は、1つの値の 0 と 1 をすべて反転します。

ここから分かることは、ビット演算子はどれも2進数の各けたを対象にしているものの、何を 1 として残すかのルールがそれぞれ違うということです。

ビット演算子は整数型に対して使う

ビット単位の論理演算子は、整数型の値に対して使います。

たとえば、int や short などの整数型を2進数として見て、そのビットを操作します。

ここで大切なのは、ビット演算子は true や false を組み合わせる論理演算子とは別物だということです。

演算子主な対象役割
&&true と false両方 true なら true
||true と falseどちらか true なら true
!true と falsetrue / false を反転する
&整数のビット両方 1 のビットを 1 にする
|整数のビットどちらか 1 のビットを 1 にする
^整数のビット異なるビットを 1 にする
~整数のビット0 と 1 を反転する

&& と & は、記号が似ています。
しかし、扱う対象が違います。

&& は、条件式の結果である true / false を扱います。
& は、整数の中にあるビットを扱います。

同じように、|| と | も区別が必要です。

ビット演算子を学ぶ意味

ビット演算子は、Javaの初学者にとって少し特別で難しく感じるかもしれません。

普段のプログラムでは、10進数の数値をそのまま扱うことが多いからです。

しかし、ビット演算子を学ぶと、コンピュータが数値をどのように扱っているのかが見えやすくなります。

ビット演算子は、たとえば次のような場面で使われることがあります。

場面イメージ
フラグの管理複数の状態を1つの数値にまとめる
権限の表現読み取り、書き込み、実行などをビットで管理する
特定のビットだけを取り出す必要な印だけを確認する
数値を低レベルで効率よく扱う0と1の単位で操作する

今の段階では、まず「ビット演算子は2進数の各けたを操作する演算子」と理解できれば十分です。

ドラゴンボール風にたとえると、ビット演算子は、戦士の任務札に刻まれた細かな印を確認したり、必要な印だけを取り出したりする技です。

普段は札の表に書かれた番号だけを見れば十分でも、細かい制御をしたいときには裏側の印を見る必要があります。

図:ビット演算子は数値の裏側の印を操作する

この図が示していること

この図では、ビット演算子が数値の裏側にある 0 と 1 の並びを操作することを示しています。

表側では、数値は 5 や 12 のような10進数として見えます。
しかし、コンピュータの中では 00000101 や 00001100 のような2進数として扱われます。

&、|、^、~ は、その2進数の各けたを対象にして操作します。

ここから分かることは、ビット演算子は普段見えている数値そのものではなく、数値の内部にあるビットの並びを操作する演算子だということです。

論理演算子との違いをもう一度整理する

ビット演算子で特に混同しやすいのは、論理演算子との違いです。

演算子主な対象役割
&&true と false両方 true なら true
||true と falseどちらか true なら true
!true と false反転する
&整数のビット両方 1 のビットを 1 にする
|整数のビットどちらか 1 のビットを 1 にする
^整数のビット異なるビットを 1 にする
~整数のビット0 と 1 を反転する

この表を見ると、&&、||、! は条件の世界で使う演算子だと分かります。

一方、&、|、^、~ は、整数を2進数として見たビットの世界で使う演算子です。

記号が似ていても、対象が違います。

見分け方内容
条件を扱っている&&、
整数のビットを扱っている&、
0 と 1 の各けたを見ているビット演算子
true / false を見ている論理演算子

ビット演算子を理解するための流れ

ビット演算子を読むときは、次の流れで考えると整理しやすくなります。

順番考え方
1数値を2進数で見る
2桁数をそろえる
3各けたを1つずつ比べる
4演算子ごとのルールを当てはめる
5結果の2進数を10進数に戻して考える

たとえば、5 & 12 なら、次の流れです。

手順内容
15 を 00000101 と見る
212 を 00001100 と見る
3同じ位置のビットを比べる
4両方 1 のところだけ 1 にする
5結果 00000100 を 10進数の 4 と見る

この順番で考えると、ビット演算はかなり追いやすくなります。

ドラゴンボール風にたとえると、まず任務札の表の番号を見る。
次に裏側の印を見る。
そして同じ位置の印を見比べ、技ごとのルールで印を残す、という流れです。

最初に覚えたいルール

ビット演算子を学び始めるときは、まず次の4つを押さえましょう。

演算子最初に覚えるルール
&両方 1 のときだけ 1
|どちらかが 1 なら 1
^異なるときだけ 1
~0 と 1 を反転する

この4つを覚えるだけでも、ビット演算子の基本はかなり見えてきます。

ドラゴンボール風にたとえるなら、次のように覚えられます。

演算子ドラゴンボール風のイメージ
&両方の戦士が持つ共通の印だけ残す
|どちらかが持つ印を全部集める
^違っている印だけを光らせる
~印の有無をすべて反転する

ビット演算子は、普段の条件分岐とは少し違う角度から数値を見る内容です。

けれど、数値の中身を 0 と 1 の並びとして見られるようになると、コンピュータらしい考え方に一歩近づけます。

Javaの数値は、表では10進数として見えていても、内部では0と1の世界とつながっています。
ビット演算子は、その世界を直接操作するための基本的な道具です。