Opérateurs bit à bit en Java
Manipulez les bits individuels en Java avec &, |, ^, ~, <<, >> et >>> grâce aux opérateurs bit à bit.
La plupart du code Java ne touche pas aux bits individuels. Mais de temps en temps — regrouper des drapeaux dans un int, lire un format de fichier binaire, calculer un hachage, travailler avec des masques de bits de permission — vous aurez besoin de manipuler des valeurs au niveau des bits. Les opérateurs bit à bit de Java sont le jeu de style C : &, |, ^, ~ et les trois décalages. Ils sont distincts des opérateurs logiques && et ||, qui utilisent le court-circuit et ne fonctionnent qu'avec des valeurs boolean — les opérateurs bit à bit agissent sur chaque bit d'un entier.
Les opérateurs
| Opérateur | Nom | Ce qu'il fait |
|---|---|---|
& | ET (AND) | le bit est 1 uniquement si les deux bits sont 1 |
| | OU (OR) | le bit est 1 si l'un ou l'autre bit est 1 |
^ | OU exclusif (XOR) | le bit est 1 si les deux bits sont différents |
~ | NON (complément) | inverse tous les bits |
<< | décalage à gauche | décale les bits vers la gauche, remplit avec 0 à droite |
>> | décalage à droite signé | décale à droite, remplit avec le bit de signe à gauche |
>>> | décalage à droite non signé | décale à droite, remplit avec 0 à gauche |
Tous opèrent sur des opérandes int et long. Les types byte, short et char sont d'abord promus en int.
Les littéraux binaires (0b...) facilitent la visualisation des motifs de bits :
int a = 0b1100; // 12
int b = 0b1010; // 10
System.out.println(Integer.toBinaryString(a & b)); // 1000 (8)
System.out.println(Integer.toBinaryString(a | b)); // 1110 (14)
System.out.println(Integer.toBinaryString(a ^ b)); // 110 (6)Notez que Integer.toBinaryString supprime les zéros non significatifs — 6 s'affiche comme 110 et non 0110. Si vous avez besoin d'une largeur fixe pour l'affichage, complétez manuellement.
NOT — ~
~ inverse tous les bits, y compris le bit de signe. Pour un int 32 bits, c'est le complément à deux : ~x est égal à -x - 1 :
System.out.println(~0); // -1
System.out.println(~5); // -6
System.out.println(~-1); // 0Les décalages
<< décale vers la gauche, multipliant par des puissances de 2 :
System.out.println(1 << 0); // 1
System.out.println(1 << 1); // 2
System.out.println(1 << 4); // 16>> décale vers la droite en préservant le signe — utile pour diviser des entiers signés :
System.out.println(16 >> 2); // 4
System.out.println(-16 >> 2); // -4 — sign extended>>> décale vers la droite et remplit toujours avec des zéros — utile quand vous traitez un int comme des bits non signés :
System.out.println(-1 >>> 28); // 15
System.out.println(-1 >> 28); // -1Utilisations pratiques
Masques de bits (flags)
Regroupez plusieurs indicateurs oui/non dans un seul int :
final int READ = 1 << 0; // 0001
final int WRITE = 1 << 1; // 0010
final int EXECUTE = 1 << 2; // 0100
int perms = READ | WRITE; // set both
boolean canRead = (perms & READ) != 0; // true
boolean canExecute = (perms & EXECUTE) != 0; // false
perms |= EXECUTE; // grant execute
perms &= ~WRITE; // revoke write
perms ^= READ; // toggle readC'est la même idée que les permissions de fichiers Unix.
Multiplier ou diviser par des puissances de 2
x << n correspond à x * 2ⁿ ; x >> n correspond à x / 2ⁿ (pour x non négatif) :
int doubled = x << 1;
int halved = x >> 1;Le compilateur optimise généralement lui-même les multiplications et divisions simples par des puissances de 2 constantes en décalages, donc écrivez ce qui est le plus lisible.
Échanger deux entiers sans variable temporaire
Un classique avec XOR :
int a = 5, b = 3;
a ^= b;
b ^= a;
a ^= b;
System.out.println(a + " " + b); // 3 5Élégant, mais rarement préférable à l'utilisation d'une variable temporaire — les compilateurs modernes gèrent très bien le cas avec temporaire.
Une démonstration
Quand utiliser ces opérateurs vs. EnumSet
Pour un petit ensemble fixe de drapeaux en Java moderne, EnumSet<MyFlag> est généralement plus lisible et tout aussi efficace — il stocke les valeurs d'énumération sous forme d'un seul masque de bits long en interne, vous offrant ainsi la lisibilité de Set<MyFlag> avec des opérations rapides au niveau des bits :
enum Permission { READ, WRITE, EXECUTE }
EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
perms.add(Permission.EXECUTE);
perms.contains(Permission.READ); // trueUtilisez les opérations bit à bit brutes uniquement lorsque vous manipulez des formats binaires, des registres matériels, ou des chemins critiques où l'empaquetage en int est important.
Prochaine étape
Les chaînes Java — le type référence avec lequel vous travaillerez plus que tout autre.