HOWTO · Java

Convertir matriz de bytes a entero en Java

Este tutorial demuestra cómo convertir un array de bytes a int en Java

En esta página

Convertir un array de bytes en un entero en Java es una tarea habitual al trabajar con protocolos de red, E/S de archivos o formatos de datos binarios. El enfoque adecuado depende de lo que representen los bytes: a veces varios bytes codifican un único int y otras veces cada byte es un valor independiente. Este artículo explica los métodos principales de Java — ByteBuffer, DataInputStream, el desplazamiento manual de bits y BigInteger — además de las reglas de orden de bytes y signo que determinan si el resultado es correcto.

Convertir cuatro bytes en un int con ByteBuffer

ByteBuffer (en java.nio) es la herramienta más directa del núcleo de Java para esta conversión. El método wrap crea un búfer que comparte el almacenamiento del array, y getInt() lee cuatro bytes a partir de la posición actual del búfer y los interpreta como un único int. Ejecuta este ejemplo (el código y el mensaje que imprime permanecen en inglés):

import java.nio.ByteBuffer;

public class ByteArrayToInt {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = ByteBuffer.wrap(byteArray).getInt();

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Salida:

Converted Integer Value: 66320

Un ByteBuffer es big-endian de forma predeterminada en todas las plataformas: el primer byte del array se trata como el byte más significativo. Si los datos se escribieron en orden little-endian, selecciona ese orden explícitamente con ByteOrder antes de leerlos (las etiquetas del ejemplo permanecen en inglés):

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ByteArrayToIntOrder {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int bigEndianValue = ByteBuffer.wrap(byteArray).getInt();
    int littleEndianValue =
        ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN).getInt();

    System.out.println("big-endian: " + bigEndianValue);
    System.out.println("little-endian: " + littleEndianValue);
  }
}

Salida:

big-endian: 66320
little-endian: 268632320

Aquí importan dos detalles. En primer lugar, getInt() lee desde la posición actual del búfer y la avanza en cuatro, por lo que llamarlo dos veces sobre el mismo búfer lee los cuatro bytes siguientes; un wrap nuevo siempre comienza en la posición 0. En segundo lugar, cuando quedan menos de cuatro bytes, getInt() lanza una BufferUnderflowException, así que comprueba la longitud de la entrada antes de leer.

Leer cuatro bytes con DataInputStream

Cuando los bytes llegan a través de un flujo —o cuando quieres leer varios valores primitivos en secuencia— DataInputStream es una buena opción. Envuelve el array en un ByteArrayInputStream, envuélvelo a su vez en un DataInputStream y llama a readInt() (el código y el mensaje que imprime permanecen en inglés):

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

public class ByteArrayToIntWithDataInputStream {
  public static void main(String[] args) throws IOException {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    try (DataInputStream input =
        new DataInputStream(new ByteArrayInputStream(byteArray))) {
      int intValue = input.readInt();
      System.out.println("Converted Integer Value: " + intValue);
    }
  }
}

Salida:

Converted Integer Value: 66320

readInt() siempre interpreta los cuatro bytes siguientes en orden big-endian, independientemente de la plataforma. Cuando quedan menos de cuatro bytes en el flujo, lanza una EOFException, que puedes capturar para informar de una entrada incompleta en lugar de que el programa falle (el ejemplo y su mensaje permanecen en inglés):

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;

public class ByteArrayToIntShortInput {
  public static void main(String[] args) throws IOException {
    byte[] byteArray = {0x03, 0x10};

    try (DataInputStream input =
        new DataInputStream(new ByteArrayInputStream(byteArray))) {
      int intValue = input.readInt();
      System.out.println("Converted Integer Value: " + intValue);
    } catch (EOFException e) {
      System.out.println("readInt() needs four bytes, but only " + byteArray.length + " remain");
    }
  }
}

Salida:

readInt() needs four bytes, but only 2 remain

Construir un int desplazando bytes

Desplazar los bytes manualmente muestra exactamente cómo funciona la conversión y ofrece control total sobre el orden y la longitud. Para cada byte, desplaza el valor acumulado 8 bits a la izquierda y combínalo con el byte siguiente mediante OR (el código y el mensaje que imprime permanecen en inglés):

public class ByteArrayToIntManually {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = 0;

    for (byte b : byteArray) {
      intValue = (intValue << 8) | (b & 0xFF);
    }

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Salida:

Converted Integer Value: 66320

La máscara b & 0xFF es necesaria porque el tipo byte de Java tiene signo: 0x80 se almacena como -128. Sin la máscara, | combinaría 0xFFFFFF80 en lugar de 0x00000080, lo que corrompería todos los bytes superiores. Este bucle ensambla los bytes en orden big-endian —el primer elemento del array es el byte más significativo— y supone que el array contiene exactamente cuatro bytes. Para usar otro orden, recorre el array en dirección opuesta; para más bytes, acumula en un long en lugar de un int.

Interpretar un array de bytes con BigInteger

BigInteger acepta un array de bytes directamente en su constructor y lo interpreta como un número con signo, big-endian y en complemento a dos, de longitud arbitraria. Después, intValue() reduce el resultado a un int (el código y el mensaje que imprime permanecen en inglés):

import java.math.BigInteger;

public class ByteArrayToIntWithBigInteger {
  public static void main(String[] args) {
    byte[] byteArray = {0x00, 0x01, 0x03, 0x10};

    int intValue = new BigInteger(byteArray).intValue();

    System.out.println("Converted Integer Value: " + intValue);
  }
}

Salida:

Converted Integer Value: 66320

Como la interpretación tiene signo, un bit inicial 1 produce un valor inferior a 0; pasa un signum de 1 como primer argumento del constructor para obtener la interpretación sin signo. Ten en cuenta que intValue() conserva únicamente los 32 bits inferiores, por lo que trunca silenciosamente los valores que no caben en un int (las etiquetas del ejemplo permanecen en inglés):

import java.math.BigInteger;

public class BigIntegerSignAndRange {
  public static void main(String[] args) {
    byte[] allOnes = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};

    System.out.println("signed: " + new BigInteger(allOnes));
    System.out.println("unsigned: " + new BigInteger(1, allOnes));
    System.out.println("narrowed to int: " + new BigInteger(1, allOnes).intValue());
  }
}

Salida:

signed: -1
unsigned: 4294967295
narrowed to int: -1

BigInteger es la opción natural cuando el array tiene más de cuatro bytes o cuando necesitas la magnitud sin signo antes de reducirla.

Convertir cada byte por separado

Todos los métodos anteriores combinan varios bytes en un único número. Si cada byte es un valor independiente —lecturas de sensores, canales de color o campos de protocolo— convierte los elementos individualmente. Observa la diferencia: getShort() o readShort() sobre dos bytes produce un único short combinado; no convierte cada byte por separado.

Recuerda también que el tipo byte de Java tiene signo y va de -128 a 127. Cuando lo que necesitas es un valor sin signo de 0 a 255, usa Byte.toUnsignedInt (disponible desde Java 8) o la máscara equivalente b & 0xFF (las etiquetas del ejemplo permanecen en inglés):

public class PrintEachByte {
  public static void main(String[] args) {
    byte[] byteArray = {0x03, 0x04, (byte) 0x80, (byte) 0xFF};

    for (byte b : byteArray) {
      System.out.println("signed: " + b + " unsigned: " + Byte.toUnsignedInt(b));
    }
  }
}

Salida:

signed: 3 unsigned: 3
signed: 4 unsigned: 4
signed: -128 unsigned: 128
signed: -1 unsigned: 255

Elegir un método y gestionar casos límite

Cada enfoque se adapta a una situación distinta:

  • ByteBuffer es la primera opción para leer un int de cuatro bytes desde un array: es compacto, admite ambos órdenes de bytes mediante ByteOrder y proporciona getLong(), getShort() y métodos similares para otros tamaños.
  • DataInputStream resulta adecuado para código que ya trabaja con flujos y necesita leer varios valores con tipo en secuencia; sus lecturas siempre son big-endian.
  • El desplazamiento manual requiere algunas líneas más, pero controla con precisión el orden, el desplazamiento inicial y el tamaño, sin necesidad de crear un búfer ni un flujo.
  • BigInteger gestiona arrays de más de cuatro bytes y permite una interpretación explícita con o sin signo antes de reducir el valor.
  • La conversión byte a byte con Byte.toUnsignedInt se aplica cuando cada elemento es un valor propio y no parte de un único número.

Elijas el método que elijas, comprueba estos puntos antes de convertir:

  • Longitud de la entrada: una lectura de cuatro bytes sobre un array más corto falla: BufferUnderflowException para ByteBuffer, EOFException para DataInputStream, y una ArrayIndexOutOfBoundsException o un resultado incorrecto sin aviso en el código manual.
  • Orden de bytes: el orden usado al leer debe coincidir con el orden en que se escribieron los bytes; ByteBuffer usa big-endian de forma predeterminada y puede cambiarse con ByteOrder, mientras que las lecturas de DataInputStream siempre son big-endian y no ofrecen una opción de orden.
  • Signo y rango: getInt() y readInt() devuelven un int con signo, por lo que un valor de 32 bits sin signo superior a Integer.MAX_VALUE aparecerá como un número inferior a 0. Usa Integer.toUnsignedLong(intValue) o acumula en un long cuando los datos de origen no tengan signo.
  • Posición del búfer: reutilizar un ByteBuffer continúa desde su posición actual, no desde el principio; llama a rewind() o vuelve a usar wrap para releer el array.