はじめに
Javaの^は、整数値のビットを比較して異なる位置だけを1にするXOR演算子です。一方、boolean同士にも使えるため、条件が片方だけ成り立つかを表すプログラミングでも扱えます。
これを理解すると、ビット反転、フラグ切り替え、パリティ計算、簡易的なエンコード、配列内の一意値検出などの使い方を整理できます。ただし、符号付き整数や暗号用途には注意点があるため、サンプルコードと期待される出力を合わせて確認する構成にしているのが基本です。
- Java SE 21 / javac 21
- 標準ライブラリのみ使用
- XOR演算子の真理値と
^の読み方 - 整数、文字、配列でのサンプルコードの読み解き方
- ビットマスク、シフト演算、パリティ計算の使い方
- 符号付き整数や簡易暗号で起きやすい注意点
- 用途に合わせたカスタマイズの考え方
公式ドキュメントでは、ビット単位の演算子として&、|、^、~、<<、>>、>>>が扱われます。詳細な仕様はJava Language Specification 15.22とInteger APIを基準にすると、型変換や符号なし比較の判断が安定します。
JavaとXOR演算子の基本
JavaでXOR演算子を使うときの結論は、a ^ bがビット位置ごとの排他的論理和を返す、という一点に集約されますし、ここがポイントです。そのため、同じビットなら0、異なるビットなら1になると覚えると、後続のサンプルコードも読みやすくなります。
結果: 期待される出力は、2進数の0110に相当する10進数の6です。
この計算では、左から順に1と1が同じなので0、0と1が異なるので1になります。ただし、紙に書いたビット列とJavaのintは桁数が異なるため、実際のプログラミングでは型の幅も意識するのが目安です。
XOR演算子の真理値
| 入力A | 入力B | A ^ B | 意味 |
|---|---|---|---|
0 | 0 | 0 | 同じ値なので偽 |
0 | 1 | 1 | 片方だけ真 |
1 | 0 | 1 | 片方だけ真 |
1 | 1 | 0 | 同じ値なので偽 |
x | 0 | x | 0とのXORは値を保つ |
x | x | 0 | 同じ値は相殺される |
x | mask | 反転結果 | マスクの1位置だけ変わる |
この表の性質は、XOR演算子を使ったフラグ操作や一意値検出の土台になります。特にx ^ xが0になり、x ^ 0がxのまま残る点は、アルゴリズム系のプログラミングで頻繁に使われます。
JavaにおけるXORの基本文法
Javaでは、整数型のbyte、short、char、int、longに対して^を使えるのがポイントです。一方、boolean同士の^は論理XORとして扱われ、片方だけがtrueのときにtrueになります。
結果: 期待される出力はXORの結果は 6 です。です。
その理由は、5が2進数で101、3が011になり、各桁を比較すると110になるためです。この110を10進数に戻すと6になり、XOR演算子の基本的な使い方を確認できます。
関連する型の扱いを深めたい場合は、Java List型完全ガイドのようにデータ構造側の知識も合わせて読むと、配列やコレクションに対する処理の見通しが良くなります。一方、注釈ベースの設計に関心がある場合は、Javaアノテーションの解説が構文理解の補助になるのが一般的です。
💡 Tips:^は累乗ではありません。Javaで累乗を扱う場合はMath.pow()を使い、XOR演算子とは役割を分けて考えます。
XOR演算子の具体的な使い方
基本の真理値が分かると、XOR演算子の使い方はビットの切り替えや相殺として整理できます。具体的には、maskを使って一部のビットを反転し、同じ値を二度XORして元に戻す流れが中心です。
サンプルコード1:XORを用いたビット反転
ビット反転では、反転したい位置だけが1になったマスクを用意し、そのマスクと対象値をXORするのが現実的です。ただし、元記事のサンプルコードは0xFFFFを使うため、下位16ビットを対象にした反転として読むのが自然です。
結果: 期待される出力はビット反転後の数値は 65530 です。です。
このサンプルコードでは、aの下位ビットとbの1が重なる位置だけ反転します。そのため、5の下位16ビット表現に対して0xFFFFを適用し、期待される値が65530になります。
サンプルコード2:2つの数値のスワップ
XORを使ったスワップは、一時変数なしで値を入れ替える古典的な使い方です。一方、読みやすさではtemp変数を使う通常の入れ替えが勝るため、仕組みの理解用として扱うのが現実的です。
結果: 期待される出力は、先にスワップ前:x = 10, y = 20、後にスワップ後:x = 20, y = 10です。
この処理では、最初のx = x ^ yで両方の情報をxへ混ぜ込みます。その後のy = x ^ yで元のxを取り出し、最後のx = x ^ yで元のyを復元します。
ただし、同じ変数を参照するケースや可読性を重視する業務コードでは、int temp = x;を使う方法のほうが意図を伝えやすいです。XOR演算子の学習では、値が相殺される性質を体感するサンプルコードとして位置づけると理解しやすくなると整理できます。
サンプルコード3:特定のビットを取得
特定のビットを取得する処理では、実際にはXORではなく&と>>が中心になります。そのため、この例はXOR演算子そのものの使い方というより、ビット演算全体の基礎として読む必要があります。
結果: 期待される出力は3番目のビットは 1 です。です。
このサンプルコードでは、1 << (pos - 1)で目的の位置だけが1のmaskを作ります。そのmaskとnumを&で比較し、右シフトで最下位へ寄せるため、取得結果が0または1になります。
このようなビット操作は、フラグ管理や通信データの解析で見かけますが、これは押さえたい点です。うるう年判定のような条件分岐を学ぶ場合はJavaでうるう年を判定する解説も参考になり、条件式とビット演算の違いを比較できます。
サンプルコード4:XORを使ったエンコードとデコード
XORには、同じキーを二度適用すると元に戻る自己反転性があります。そのため、簡易的なエンコードとデコードの説明では使われますが、安全な暗号として扱うには不十分です。
結果: 期待される出力は、エンコード後の文字:に変換後の文字、デコード後の文字:Aに元の文字が表示される形です。
この処理では、original ^ keyで作ったencryptedに、同じkeyを再度XORしています。そのため、途中の文字が読みにくい値になっても、decryptedは元の'A'へ戻ります。
サンプルコード5:配列内の一意な数値を探す
配列内で同じ数値が必ずペアになり、ひとつだけ単独の値がある場合、XOR演算子で一意な値を取り出せます。これはx ^ xが0になり、0 ^ yがyになる性質を利用したプログラミング例です。
結果: 期待される出力は配列内の一意な数値は 4 です。です。
その配列では1、2、3、5がそれぞれ二度登場し、XORで相殺されます。一方、4だけは相手がいないため、uniqueNumberに残ります。
この使い方は、追加のHashMapやHashSetを使わずにforループだけで処理できると覚えるとよいでしょう。ただし、単独値が複数ある配列や、ペアにならない値が混ざる配列では前提が崩れるため、入力条件の確認が欠かせません。
XOR演算子の応用例
応用例では、XOR演算子を単体で覚えるより、文字列、圧縮、論理ゲート、エラー検出、条件式との組み合わせとして見るほうが理解しやすくなります。そのため、各サンプルコードでは、どの性質を使っているかを短く押さえます。
サンプルコード6:暗号化アルゴリズムの基本
文字列全体にXORをかける場合は、Stringをchar[]に変換し、各文字へ同じキーを適用すると考えられます。このサンプルコードは仕組みの説明に向いた簡易版であり、実運用の暗号設計とは分けて扱います。
結果: 期待される出力は、暗号化後:に変換後の文字列、復号後: Hello, World!に元の文字列が表示される形です。
このコードでは、encrypt(String input, char key)がtoCharArray()で文字列を配列へ変換し、encrypt(char[] input, char key)へ処理を渡します。同様に復号でも同じkeyを使うため、XOR演算子の自己反転性によって元の文字列へ戻ります。
ただし、char単位の処理はUnicodeの結合文字やサロゲートペアを考慮しません。文字列処理全般のエスケープを学ぶ場合は、Javaエスケープ処理の解説も合わせると、表示文字と内部表現の違いを整理できると言えるでしょう。
サンプルコード7:XORを利用したデータ圧縮
データ圧縮の例では、連続する文字数を数えるRLE風の処理にXORを混ぜています。一方、このコードは一般的な圧縮形式ではなく、XORのカスタマイズ例として読むのが適切です。
結果: 期待される出力は圧縮後:に変換後の文字列が続く形式です。
このサンプルコードでは、連続回数のcountを'0'とXORしてからStringBuilderへ追加します。ただし、可逆な圧縮として使うには復元処理や入力制約の設計が必要になるため、学習用の変換例として扱います。
サンプルコード8:XORゲートのシミュレーション
XORゲートは、入力が異なるときだけ出力が1になる論理回路です。その真理値はXOR演算子と同じなので、Javaのforループで全組み合わせを出力できるのが基本です。
結果: 期待される出力は、AとBが異なる行だけ出力が1になる真理値表です。
この処理では、外側のforがA、内側のforがBの値を変えます。その組み合わせに対してA ^ Bを計算するため、XORゲートの動作を小さなプログラムで表せます。
結果: 期待される表示は、同じ入力の行が0、異なる入力の行が1になる表です。
この表は、整数のビット演算だけでなく、booleanの論理判定にも対応するのが目安です。たとえばtrue ^ falseはtrueになり、true ^ trueはfalseになります。
サンプルコード9:エラー検出の基本ロジック
パリティビットは、データ中の1の個数が偶数か奇数かを示す値です。XOR演算子を順に適用すると、1の個数が奇数なら1、偶数なら0が残ります。
結果: 期待される出力はパリティビット: 1です。
このサンプルコードでは、parityの初期値を0にして、配列dataの各ビットを順にXORします。そのため、1が三個ある入力では奇数パリティとして1が残ります。
ただし、単純なパリティでは複数ビットの誤りをすべて検出できるわけではありません。通信や保存データの検査では、CRCやハッシュなど、目的に応じた方式を選ぶ必要があるのがポイントです。
サンプルコード10:条件なしでの数値の最大値の検出
ビット演算と条件演算子を組み合わせると、分岐を減らした最大値計算を書けます。ただし、読みやすさを優先する通常のJavaプログラミングでは、Math.max()や明示的な比較を選ぶほうが理解されやすいです。
結果: 期待される出力は最大値: 9です。
この式では、x < y ? 1 : 0がtrueなら1、そうでなければ0になります。その値にマイナスを付けると、-1または0のマスクになり、x ^ yの反映範囲が切り替わります。
一方、式の意図が伝わりにくい点は明確な注意点です。継承やメソッド上書きの読みやすさまで意識するなら、Javaでマスターするオーバーライド解説のように、意図をコード構造で伝える考え方も役立ちますし、これが一つの目安です。
XOR演算子を使用する際の注意点と対処法
XOR演算子の注意点は、式が短いほど意図も短く見えてしまうことです。しかし、符号、型変換、文字コード、暗号強度、入力条件が絡むと結果の解釈が難しくなるため、サンプルコードの前提を明記する必要があります。
XORの罠とその回避方法
Javaのintは符号付き32ビット整数で、負数は2の補数で表されます。そのため、負数と正数をXORすると、見た目の10進数だけでは直感しにくい結果になるのが一般的です。
結果: 期待される出力はXOR結果: -4です。
この結果は、-10の2の補数表現と10のビット列をXORした値を、再び符号付きintとして解釈するために生じます。元記事では符号なし整数という表現が出ますが、Javaのint自体は符号付きであり、符号なし風の比較や表示には専用メソッドを使います。
そのため、負数を含むXOR演算子の使い方では、Integer.toBinaryString()でビット列を表示して確認すると混乱を減らせますが、覚えておくと役立つでしょう。ただし、これはデバッグ補助であり、仕様の根拠はJava言語仕様と標準APIに置くのが安全です。
ビット演算のベストプラクティス
ビット演算の実装では、演算対象の型、期待する桁数、マスクの意味を変数名で表すと読みやすくなります。たとえばmask、flags、parity、encoded、decodedのような名前は、値の役割を示します。
符号なしの大小比較が必要な場合は、Integer.compareUnsigned()やLong.compareUnsigned()を検討するのが現実的です。これらは値を符号なしとして比較するため、ビット列としての大小関係を扱いたい場面に向きます。
結果: 期待される出力は比較結果: 等しくないです。
このサンプルコードでは、x ^ yの結果を0と符号なし比較しています。結果が0でなければビット列が一致していないため、表示は等しくないになります。
compareUnsignedは値を変換するメソッドではなく、比較時の解釈を変えるメソッドです。表示形式を変えたい場合はInteger.toUnsignedString()などを使いると整理できます。具体的には、XOR演算子を使うコードでは、^=のような複合代入演算子を多用しすぎないほうが読みやすくなります。短いプログラミング問題ではuniqueNumber ^= numが自然でも、業務処理では途中値の意味をコメントや変数名で補うと保守しやすくなります。
XOR演算子のカスタマイズ方法
カスタマイズでは、どのビットを反転させたいか、どの範囲を集計したいか、どの形式で結果を見せたいかを先に決めますし、ここを基本と考えるとよいでしょう。その設計が曖昧なままXOR演算子を使うと、短い式でも意味が追いにくくなります。
カスタムビットマスクの作成
カスタムビットマスクは、反転したい位置だけを1にした値です。たとえば6は2進数で110に相当し、対象値の下位2番目と3番目に作用します。
結果: 期待される出力は適用後の数値: 27です。
この例では、29の2進数表現11101に対して、00110に相当するマスクをXORします。その結果、該当するビットだけが反転し、10進数では27になります。
使い分けると、ビットを必ず1にしたい場合は|、必ず0にしたい場合は& ~mask、反転したい場合は^が合いると理解できます。XOR演算子のカスタマイズは、この違いを意識すると誤用を減らせます。
XORの拡張操作
複数の数値をまとめてXORすると、値の相殺やパリティ的な集約ができます。一方、合計や平均とは意味が異なるため、集計結果を数値の大小や総量として解釈しないようにすると覚えるとよいでしょう。
結果: 期待される出力はXOR結果の集合: 1です。
このサンプルコードでは、xorResultに配列の各要素を順にXORしていきます。1 ^ 2 ^ 3 ^ 4 ^ 5の結果が1になるため、最終的な表示もその値になります。
こうしたカスタマイズは、重複検出、簡易チェック、フラグ集約などに応用できると考えられます。ただし、入力の順序に依存しない一方で、どの値がどのように相殺されたかは途中情報として残らないため、ログや検査用コードを別に用意すると扱いやすくなります。
まとめ
JavaのXOR演算子は、^でビット単位の排他的論理和を計算し、同じ値を相殺し、異なるビットを残す演算子です。その性質を押さえると、ビット反転、スワップ、一意値検出、パリティ計算、簡易エンコードなどの使い方を一貫して理解できます。
ただし、XOR演算子には注意点もあると言えるでしょう。負数を含むintでは2の補数表現が関係し、暗号用途ではXORだけでは安全性が足りず、圧縮や最大値計算の例では可読性や前提条件を明確にする必要があります。
そのため、実装時はmask、flags、parity、uniqueNumberのように役割を表す名前を使い、必要に応じてInteger.compareUnsigned()やInteger.toBinaryString()を併用します。カスタマイズを行う場合も、反転、設定、解除、比較のどれをしたいのかを分けると、プログラミング上の誤解を避けやすくなるのが基本です。
サンプルコードを読み返すと、XOR演算子は単なる記号ではなく、入力条件と型の解釈に支えられた道具だと分かります。Javaでビット演算を扱う場面では、期待される出力だけでなく、どのビットが変わったのかまで確認する習慣が理解を助けます。
関連記事
- Java List型完全ガイド!初心者でもマスターできる7つのステップ
- Javaアノテーションの12選!初心者から上級者まで徹底ガイド
- Javaでうるう年を判定!初心者でも分かる9ステップ解説
- Javaエスケープ処理の10ステップマスターガイド
- Javaでマスターする!オーバーライドのたった7つのステップ
※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。


