Operatori bitwise in Java
In questa lezione parliamo degli operatori bitwise in Java, un concetto interessante che ti permette di lavorare direttamente sui bit, i mattoni fondamentali dei dati digitali.
A cosa servono? Quando usi gli operatori bitwise, manipoli i singoli bit che compongono i numeri, anziché il numero intero nel suo insieme. Del resto 'bitwise' significa 'bit a bit'. Ad esempio, il numero intero 5 in binario è composto da 4 bit `0101`. Questo può sembrare complesso da capire, ma non ti preoccupare, vedrai che con qualche esempio diventa tutto più chiaro.
Ora ti starai sicuramente chiedendo cosa sono i bit?
Immagina di avere alcuni interruttori, e ogni interruttore può essere acceso o spento: acceso è 1, spento è 0. Questi interruttori sono i bit in un numero binario.
Bene, gli operatori bitwise ti permettono di "giocare" con questi interruttori.
I principali operatori bitwise in Java sono:
| Operatore | Descrizione | Esempio |
|---|---|---|
| & | AND bitwise | a & b |
| | | OR bitwise | a | b |
| ^ | XOR bitwise | a ^ b |
| ~ | NOT bitwise | ~a |
| << | Shift a sinistra | a << 2 |
| >> | Shift a destra | a >> 2 |
| >>> | Shift a destra con riempimento a zero | a >>> 2 |
| >>= | Shift a destra con assegnazione | a >>= 2 |
| <<= | Shift a sinistra con assegnazione | a <<= 2 |
Vediamoli uno per uno e cerchiamo di capire come funzionano con alcuni esempi pratici.
Operatore AND (`&`)
L'operatore AND confronta i bit di due numeri e restituisce 1 solo se entrambi i bit corrispondenti sono 1. Altrimenti restituisce 0.
Ad esempio, scrivi questo codice
int a = 5; // In binario: 0101
int b = 3; // In binario: 0011
int result = a & b; // AND bitwise
Come funziona?
- Il numero 5 in binario è: `0101`
- Il numero 3 in binario è: `0011`
Se confronti i bit corrispondenti che si trovano nella stessa posizione, ottieni `0001` che in decimale è 1.

Quindi, in questo caso `a & b` restituisce 1.
1
Operatore OR (`|`)
L'operatore OR restituisce 1 se almeno uno dei due bit è 1, e restituisce 0 solo se entrambi i bit sono 0.
int a = 5; // In binario: 0101
int b = 3; // In binario: 0011
int result = a | b; // OR bitwise
Come funziona?
- 5 in binario è: `0101`
- 3 in binario è: `0011`
Confrontando i bit, ottieni `0111`, che in decimale è 7.

Quindi, il risultato è 7.
7
Operatore XOR (`^`)
L'operatore XOR restituisce 1 se i bit corrispondenti sono diversi, cioè uno è 1 e l'altro è 0, e restituisce 0 se i bit sono uguali.
int a = 5; // In binario: 0101
int b = 3; // In binario: 0011
int result = a ^ b; // XOR bitwise
Come funziona?
- 5 in binario è: `0101`
- 3 in binario è: `0011`
In questo caso devi mettere 1 solo se i bit nella stessa posizione sono diversi. Se invece sono uguali devi scrivere 0.

Se confronti i bit nella stessa posizione ottieni `0110`, che in decimale è il numero 6.
6
Operatore NOT (`~`)
L'operatore NOT inverte tutti i bit: trasforma i 1 in 0 e i 0 in 1.
int a = 5; // In binario: 0101
int result = ~a; // NOT bitwise
Il numero 5 in binario è: `0101`
Invertendo tutti i bit otteni `1010`.

In Java questo risultato viene trattato come un numero negativo a causa della rappresentazione con segno dei numeri (Two's Complement), quindi il risultato sarà -6.
-6
Spiegazione. In genere i numeri sul computer sono rappresentati in binario a 32 bit o a 64 bit. Ad esempio, il numero 5 a 32 bit è rappresentato in questo modo:
00000000 00000000 00000000 00000101
Quando applichi l'operatore NOT (`~`) su un numero in Java, tutti i bit del numero vengono invertiti. Invertendo tutti i bit del numero 5 ottieni
11111111 11111111 11111111 11111010
In Java, quando il bit più significativo (il primo a sinistra) è 1, il numero è interpretato come negativo. Questa è una delle caratteristiche del Two's Complement. In questo caso, per ottenere il valore decimale del numero negativo si inverte nuovamente il numero binario mantenendo in memoria il segno negativo (-).
00000000 00000000 00000000 00000101
Poi si aggiunge 1 al numero binario 101 che diventa 110 che equivale a 6 in decimale.
00000000 00000000 00000000 00000110
Sapendo che il segno è negativo, il risultato finale è -6.
Shift a sinistra (`<<`)
L'operatore di shift a sinistra sposta tutti i bit a sinistra di un certo numero di posizioni, riempiendo con 0 i nuovi bit sulla destra.
int a = 5; // In binario: 0101
int result = a << 1; // Shift a sinistra di 1 posizione
Il numero 5 in binario è: `0101`.
In questo caso dopo l'operatore '<<' è indicato 1, questo significa che vuoi spostare i bit di una posizione.

Se scorri a sinistra di una posizione diventa: `1010`, che è 10 in decimale.
10
È come moltiplicare per 2 ogni volta che fai uno shift a sinistra.
Volendo puoi indicare anche spostamenti di più posizioni. Ad esempio, 'a<<2' sposta i bit a sinistra di due posizioni, e via dicendo.
Shift a destra (`>>`)
Lo shift a destra fa esattamente l'opposto: sposta i bit a destra di un certo numero di posizioni, riempiendo con il bit del segno (se è un numero positivo, 0; se è negativo, 1).
int a = 5; // In binario: 0101
int result = a >> 1; // Shift a destra di 1 posizione
Il numero 5 in binario è: `0101`
In questo caso dopo l'operatore '>>' c'è 1, vuol dire che vuoi spostare i bit di una sola posizione verso destra.
Essendo 5 un numero positivo, gli spazi vuoti a sinistra sono riempiti con lo zero, mentre i bit che escono a destra vengono eliminati.

Se scorri i bit a destra di 1 posizione diventa: `0010`, che è 2 in decimale.
2
Shift a destra con segno (`>>>`)
Questo operatore sposta i bit a destra di un certo numero di posizioni, riempiendo con 0 i nuovi bit sulla sinistra.
E' simile allo shift a destra, ma qui i nuovi bit sono sempre riempiti con 0, indipendentemente dal segno del numero.
int a = 5; // In binario: 0101
int result = a >>> 1; // Shift a destra con segno di 1 posizione
Anche qui, il numero 5 in binario è `0101` e lo spostamento verso destra è di una posizione.

Il risultato è lo stesso del normale shift a destra: `0010`, ovvero 2.
2
Shift a destra con assegnazione (`>>=`)
L'operatore >>= è una combinazione di shift a destra con segno (>>) e assegnazione. Fa lo stesso lavoro di >>, ma assegna direttamente il risultato alla variabile su cui è applicato.
int a = 5; // In binario: 0101
a >>= 1; // Shift a destra di 1 posizione e assegna il risultato alla variabile
In questo caso i bit sono spostati a destra di una posizione e il risultato viene assegnato alla stessa variabile 'a'.
2
E' utile quando non vuoi assegnare il risultato a un'altra variabile.
Shift a sinistra con assegnazione (`<<=`)
L'operatore <<= combina lo shift a sinistra (<<) con l'assegnazione. E' simile al precedente operatore ma nel verso opposto.
int a = 5; // In binario: 0101
a <<= 1; // Shift a sinistra di 1 posizione e assegna il risultato alla variabile
In questo caso i bit sono spostati a sinistra di una posizione e il risultato viene assegnato direttamente alla stessa variabile 'a'.
10
Perché usare gli operatori bitwise?
In genere, questi operatori sono usati quando devi lavorare a basso livello con i dati, come in sistemi embedded o in situazioni dove la velocità è cruciale. Manipolare i bit direttamente è estremamente efficiente.
Ad esempio, se devi controllare un singolo bit di un numero, gli operatori bitwise sono molto più veloci rispetto a operazioni aritmetiche.
Immagina di voler capire se un numero è pari o dispari. Potresti usare l'operatore `%`, ma c'è un modo più rapido: controllare il bit meno significativo (quello più a destra) con un AND bitwise.
boolean isOdd = (a & 1) != 0;
Se il bit meno significativo è 1, il numero è dispari!
In conclusione, gli operatori bitwise possono sembrare complicati all'inizio, ma sono incredibilmente utili in molte situazioni pratiche.
Spero che ora tu abbia una visione più chiara e, chissà, magari la prossima volta che programmi in Java, penserai ai numeri come a una fila di piccoli interruttori da accendere e spegnere!