W3docs

Operatori Bitwise in Java

Manipola i singoli bit in Java con gli operatori &, |, ^, ~, <<, >> e >>> per flag, maschere e formati binari.

La maggior parte del codice Java non tocca i singoli bit. Ma ogni tanto — impacchettare flag in un int, leggere un formato di file binario, calcolare un hash, lavorare con bitmask di permessi — sarà necessario manipolare i valori a livello di bit. Gli operatori bitwise di Java sono il set in stile C: &, |, ^, ~ e i tre shift. Sono distinti dagli operatori logici && e ||, che usano il cortocircuito e funzionano solo su valori boolean — gli operatori bitwise agiscono su ogni bit di un intero.

Gli operatori

OperatoreNomeCosa fa
&ANDil bit è 1 solo se entrambi i bit sono 1
|ORil bit è 1 se almeno uno dei due bit è 1
^XORil bit è 1 se i due bit sono diversi
~NOT (complemento)inverte ogni bit
<<shift a sinistrasposta i bit a sinistra, riempie con 0 a destra
>>shift a destra con segnosposta a destra, riempie con il bit del segno a sinistra
>>>shift a destra senza segnosposta a destra, riempie con 0 a sinistra

Tutti operano su operandi int e long. byte, short e char vengono promossi a int prima.

I letterali binari (0b...) rendono i pattern di bit facili da vedere:

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)

Nota che Integer.toBinaryString elimina gli zeri iniziali — 6 viene stampato come 110, non 0110. Se hai bisogno di una larghezza fissa per la visualizzazione, aggiungila manualmente.

NOT — ~

~ inverte ogni bit, incluso il bit del segno. Per un int a 32 bit, questo è il complemento a due: ~x equivale a -x - 1:

System.out.println(~0);    // -1
System.out.println(~5);    // -6
System.out.println(~-1);   // 0

Shift

<< sposta a sinistra, moltiplicando per potenze di 2:

System.out.println(1 << 0);   // 1
System.out.println(1 << 1);   // 2
System.out.println(1 << 4);   // 16

>> sposta a destra preservando il segno — utile per dividere interi con segno:

System.out.println(16 >> 2);   // 4
System.out.println(-16 >> 2);  // -4   — sign extended

>>> sposta a destra e riempie sempre con zero — utile quando si tratta un int come bit senza segno:

System.out.println(-1 >>> 28);  // 15
System.out.println(-1 >> 28);   // -1

Usi pratici

Bitmask di flag

Impacchetta diversi flag sì/no in un singolo 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 read

È la stessa idea dei permessi sui file Unix.

Moltiplicare o dividere per potenze di 2

x << n equivale a x * 2ⁿ; x >> n equivale a x / 2ⁿ (per x non negativo):

int doubled = x << 1;
int halved  = x >> 1;

Il compilatore di solito ottimizza la moltiplicazione e la divisione semplice per potenze di 2 costanti in shift da solo, quindi scrivi quello che è più chiaro.

Scambiare due int senza una variabile temporanea

Un classico trucco XOR:

int a = 5, b = 3;
a ^= b;
b ^= a;
a ^= b;
System.out.println(a + " " + b);   // 3 5

Carino, ma raramente vale la pena usarlo rispetto a una variabile temporanea — i compilatori moderni gestiscono bene il caso con la variabile temporanea.

Una dimostrazione

java— editable, runs on the server

Quando usare questi vs. EnumSet

Per un piccolo insieme fisso di flag nel Java moderno, EnumSet<MyFlag> è di solito più leggibile e altrettanto efficiente — memorizza i valori enum come una singola bitmask long internamente, quindi ottieni la leggibilità di Set<MyFlag> con operazioni veloci a livello di bit:

enum Permission { READ, WRITE, EXECUTE }

EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
perms.add(Permission.EXECUTE);
perms.contains(Permission.READ);   // true

Ricorri alle operazioni sui bit grezzi solo quando hai a che fare con formati binari, registri hardware, o percorsi critici dove l'impacchettamento in int è importante.

Cosa viene dopo

Java Strings — il tipo di riferimento con cui lavorerai più di qualsiasi altro.

Esercitati

Pratica
Quanto vale 1 << 4?
Quanto vale 1 << 4?
Was this page helpful?