W3docs

Operadores Bit a Bit en Java

Manipula bits individuales en Java con &, |, ^, ~, <<, >> y >>> operadores bit a bit.

La mayor parte del código Java no trabaja con bits individuales. Pero de vez en cuando — al empaquetar flags en un int, leer un formato de archivo binario, calcular un hash, o trabajar con máscaras de permisos — necesitarás manipular valores a nivel de bits. Los operadores bit a bit de Java son el conjunto de estilo C: &, |, ^, ~, y los tres desplazamientos. Son distintos de los operadores lógicos && y ||, que cortocircuitan y solo funcionan con valores boolean — los operadores bit a bit actúan sobre cada bit de un entero.

Los operadores

OperadorNombreQué hace
&ANDel bit es 1 solo si ambos bits son 1
|ORel bit es 1 si cualquiera de los bits es 1
^XORel bit es 1 si los dos bits son distintos
~NOT (complemento)invierte todos los bits
<<desplazamiento izquierdodesplaza bits a la izquierda, rellena con 0 a la derecha
>>desplazamiento derecho con signodesplaza a la derecha, rellena con el bit de signo a la izquierda
>>>desplazamiento derecho sin signodesplaza a la derecha, rellena con 0 a la izquierda

Todos operan sobre operandos int y long. Los tipos byte, short y char se promueven primero a int.

Los literales binarios (0b...) facilitan la visualización de los patrones 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)

Ten en cuenta que Integer.toBinaryString elimina los ceros iniciales — 6 se imprime como 110, no como 0110. Si necesitas un ancho fijo para la visualización, deberás rellenarlo tú mismo.

NOT — ~

~ invierte todos los bits, incluido el bit de signo. Para int de 32 bits, eso es complemento a dos: ~x equivale a -x - 1:

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

Desplazamientos

<< desplaza a la izquierda, multiplicando por potencias de 2:

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

>> desplaza a la derecha preservando el signo — útil para dividir enteros con signo:

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

>>> desplaza a la derecha y siempre rellena con cero — tiene sentido cuando tratas un int como bits sin signo:

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

Usos prácticos

Máscaras de flags

Empaqueta varios flags de sí/no en un único 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

Esta es la misma idea que los permisos de archivos en Unix.

Multiplicar o dividir por potencias de 2

x << n equivale a x * 2ⁿ; x >> n equivale a x / 2ⁿ (para x no negativo):

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

El compilador normalmente optimizará la multiplicación y división simples por potencias de 2 constantes a desplazamientos por su cuenta, así que escribe lo que sea más claro.

Intercambiar dos ints sin variable temporal

Un truco clásico con XOR:

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

Curioso, pero raramente vale la pena usarlo en lugar de una variable temporal — los compiladores modernos manejan bien el caso con variable temporal.

Una demostración

java— editable, runs on the server

Cuándo usar estos vs. EnumSet

Para un conjunto pequeño y fijo de flags en Java moderno, EnumSet<MyFlag> suele ser más claro y igual de eficiente — almacena los valores enum como una única máscara de bits long internamente, por lo que obtienes la legibilidad de Set<MyFlag> con operaciones de velocidad bit a bit:

enum Permission { READ, WRITE, EXECUTE }

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

Recurre a las operaciones de bits en bruto solo cuando trabajes con formatos binarios, registros de hardware, o rutas críticas donde el empaquetado en int importa.

Qué sigue

Java Strings — el tipo de referencia con el que trabajarás más que con cualquier otro.

Práctica

Práctica
¿Cuánto vale 1 << 4?
¿Cuánto vale 1 << 4?
Was this page helpful?