¿Cómo convierto Long a byte [] y viceversa en Java?

159

¿Cómo se convierte una longa una byte[]y de vuelta en Java?

Estoy tratando de convertir a longa byte[]para poder enviarlo a byte[]través de una conexión TCP. Por otro lado, quiero tomar eso byte[]y convertirlo nuevamente en a double.

Emre801
fuente
Otra alternativa será Maps.transformValues, una herramienta general para convertir colecciones. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul
1
Consulte también stackoverflow.com/q/27559449/32453 si su objetivo es convertir un número largo en la menor cantidad de caracteres Base64.
rogerdpack
Tal vez debería enfatizarse que el canal de conversión es 'largo -> byte [] -> doble', no 'largo -> byte [] -> largo -> doble'.
Kirill Gamazkov

Respuestas:

230
public byte[] longToBytes(long x) {
    ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
    buffer.putLong(x);
    return buffer.array();
}

public long bytesToLong(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
    buffer.put(bytes);
    buffer.flip();//need flip 
    return buffer.getLong();
}

O envuelto en una clase para evitar crear repetidamente ByteBuffers:

public class ByteUtils {
    private static ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);    

    public static byte[] longToBytes(long x) {
        buffer.putLong(0, x);
        return buffer.array();
    }

    public static long bytesToLong(byte[] bytes) {
        buffer.put(bytes, 0, bytes.length);
        buffer.flip();//need flip 
        return buffer.getLong();
    }
}

Como esto se está volviendo tan popular, solo quiero mencionar que creo que es mejor usar una biblioteca como Guava en la gran mayoría de los casos. Y si tiene alguna oposición extraña a las bibliotecas, probablemente debería considerar esta respuesta primero para las soluciones nativas de Java. Creo que lo más importante que tiene mi respuesta es que no tiene que preocuparse por la endianidad del sistema usted mismo.

Brad Mace
fuente
3
Inteligente ... pero crea y descarta un ByteBuffer temporal para cada conversión. No es bueno si está enviando múltiples largos por mensaje y / o muchos mensajes.
Stephen C
1
@Stephen: estaba haciendo lo suficiente para demostrar cómo usar ByteBuffer, pero seguí adelante y agregué un ejemplo de uso en una clase de utilidad.
Brad Mace
8
Creo que el bytesToLong () aquí fallaría ya que la posición después de la colocación está al final del búfer, no al principio. Creo que obtendría una excepción de desbordamiento de búfer.
Alex Miller
11
Antes de Java 8, puede usar Long.SIZE / Byte.SIZE en lugar de Long.BYTES para evitar un número mágico.
jvdbogae
8
La reutilización de ese bytebuffer es muy problemática, y no solo por razones de seguridad de hilos como comentaron otros. No solo se necesitaría un '.clear ()' en el medio, sino que lo que no es obvio es que llamar a .array () en ByteBuffer devuelve la matriz de respaldo en lugar de una copia. Por lo tanto, si llama repetidamente y mantiene los otros resultados, en realidad son todos la misma matriz que sobrescribe repetidamente los valores anteriores. El enlace hadoop en un comentario a continuación es el más eficaz y evita cualquiera de estos problemas.
Aaron Zinman
84

Probé el método ByteBuffer contra operaciones simples a nivel de bit pero este último es significativamente más rápido.

public static byte[] longToBytes(long l) {
    byte[] result = new byte[8];
    for (int i = 7; i >= 0; i--) {
        result[i] = (byte)(l & 0xFF);
        l >>= 8;
    }
    return result;
}

public static long bytesToLong(final byte[] bytes, final int offset) {
    long result = 0;
    for (int i = offset; i < Long.BYTES + offset; i++) {
        result <<= Long.BYTES;
        result |= (bytes[i] & 0xFF);
    }
    return result;
}
Wytze
fuente
1
En lugar del resultado | = (b [i] & 0xFF); Podríamos simplemente usar result | = b [i]; como y con 0xFF por un bit no modifica nada.
Vipul
3
@Vipul Bitwise-y es importante porque al hacer solo result |= b[i]el valor de byte se convertirá primero a largo, lo que sí firma la extensión. Un byte con valor -128 (hex 0x80) se convertirá en un largo con valor -128 (hex 0xFFFF FFFF FFFF FF80). Primero después de la conversión son los valores o: ed juntos. Uso de bit a bit-y protege contra esto convirtiendo primero el byte a un int y el corte de la extensión de signo: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Por qué los bytes están firmados en Java es un poco misterioso para mí, pero supongo que encaja con otros tipos.
Lluvia de ideas
@Brainstorm Probé el caso -128 con | = b [i] y con | = (b [i] & 0xFF) y los resultados son los mismos.
Mahmoud Hanafy
El problema es que el byte se promueve antes de que se aplique el cambio, lo que causa problemas extraños con el bit de signo. Por lo tanto, primero y (&) con 0xFF para obtener el valor correcto para cambiar.
Wytze
Intento convertir estos datos (datos = nuevo byte [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8};) a largo, pero vuelve valor falso -2619032330856274424, el valor esperado es 989231983928329832
jefry jacky
29

Si está buscando una versión rápida desenrollada, esto debería funcionar, suponiendo una matriz de bytes llamada "b" con una longitud de 8:

byte [] -> largo

long l = ((long) b[7] << 56)
       | ((long) b[6] & 0xff) << 48
       | ((long) b[5] & 0xff) << 40
       | ((long) b[4] & 0xff) << 32
       | ((long) b[3] & 0xff) << 24
       | ((long) b[2] & 0xff) << 16
       | ((long) b[1] & 0xff) << 8
       | ((long) b[0] & 0xff);

largo -> byte [] como contraparte exacta de lo anterior

byte[] b = new byte[] {
       (byte) lng,
       (byte) (lng >> 8),
       (byte) (lng >> 16),
       (byte) (lng >> 24),
       (byte) (lng >> 32),
       (byte) (lng >> 40),
       (byte) (lng >> 48),
       (byte) (lng >> 56)};
Michael Böckling
fuente
1
Gracias lo mejor!
Miha_x64
15

¿Por qué necesitas el byte []? ¿Por qué no escribirlo en el zócalo?

Supongo que quiere decir largo en lugar de largo , este último debe permitir valores nulos.

DataOutputStream dos = new DataOutputStream(
     new BufferedOutputStream(socket.getOutputStream()));
dos.writeLong(longValue);

DataInputStream dis = new DataInputStream(
     new BufferedInputStream(socket.getInputStream()));
long longValue = dis.readLong();
Peter Lawrey
fuente
8
Preguntó cómo se convierte al byte [] y viceversa. Buena respuesta pero no respondió la pregunta. Pregunta por qué porque asume que es innecesario, pero esa es una suposición errónea. ¿Qué pasa si él está haciendo cross-language o multiplataforma? DataOutputStream no lo ayudará allí.
user1132959
44
Si está haciendo lenguaje cruzado o multiplataforma, entonces es importante enviar los bytes en un orden conocido. Este método hace eso (los escribe "byte alto primero") de acuerdo con los documentos. La respuesta aceptada no (los escribe en el "orden actual" según los documentos). La pregunta indica que quiere enviarlos a través de una conexión TCP. El byte[]es solo un medio para ese fin.
Ian McLaird
3
@IanMcLaird La respuesta aceptada utiliza un orden conocido. Crea una nueva ByteBufferque, según los documentos "El orden inicial de un búfer de bytes es siempre BIG_ENDIAN.
David Phillips
4

Simplemente escriba el largo en un DataOutputStream con un ByteArrayOutputStream subyacente . Desde ByteArrayOutputStream puede obtener la matriz de bytes a través de toByteArray () :

class Main
{

        public static byte[] long2byte(long l) throws IOException
        {
        ByteArrayOutputStream baos=new ByteArrayOutputStream(Long.SIZE/8);
        DataOutputStream dos=new DataOutputStream(baos);
        dos.writeLong(l);
        byte[] result=baos.toByteArray();
        dos.close();    
        return result;
        }


        public static long byte2long(byte[] b) throws IOException
        {
        ByteArrayInputStream baos=new ByteArrayInputStream(b);
        DataInputStream dos=new DataInputStream(baos);
        long result=dos.readLong();
        dos.close();
        return result;
        }


        public static void main (String[] args) throws java.lang.Exception
        {

         long l=123456L;
         byte[] b=long2byte(l);
         System.out.println(l+": "+byte2long(b));       
        }
}

Funciona para otras primitivas en consecuencia.

Sugerencia: para TCP no necesita el byte [] manualmente. Usarás un Socketsocket y sus flujos

OutputStream os=socket.getOutputStream(); 
DataOutputStream dos=new DataOutputStream(os);
dos.writeLong(l);
//etc ..

en lugar.

Michael Konietzka
fuente
4

Puede usar la implementación en org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

El código fuente está aquí:

http://grepcode.com/file/repository.cloudera.com/content/repositories/releases/com.cloudera.hbase/hbase/0.89.20100924-28/org/apache/hadoop/hbase/util/Bytes.java# Bytes.toBytes% 28long% 29

Busque los métodos toLong y toBytes.

Creo que la licencia de software le permite tomar partes del código y usarlo, pero verifíquelo.

Márquez
fuente
¿Por qué esa implementación usa XOR (^ =) en lugar de OR? github.com/apache/hbase/blob/master/hbase-common/src/main/java/…
victtim
3
 public static long bytesToLong(byte[] bytes) {
        if (bytes.length > 8) {
            throw new IllegalMethodParameterException("byte should not be more than 8 bytes");

        }
        long r = 0;
        for (int i = 0; i < bytes.length; i++) {
            r = r << 8;
            r += bytes[i];
        }

        return r;
    }



public static byte[] longToBytes(long l) {
        ArrayList<Byte> bytes = new ArrayList<Byte>();
        while (l != 0) {
            bytes.add((byte) (l % (0xff + 1)));
            l = l >> 8;
        }
        byte[] bytesp = new byte[bytes.size()];
        for (int i = bytes.size() - 1, j = 0; i >= 0; i--, j++) {
            bytesp[j] = bytes.get(i);
        }
        return bytesp;
    }
maziar
fuente
2
puede omitir la ArrayList: public static byte [] longToBytes (long l) {long num = l; byte [] bytes = nuevo byte [8]; for (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); num >> = 8; } devolver bytesp; }
eckes el
El método original no funciona con números negativos, ya que entra en un bucle infinito mientras que (l! = 0), el método de @ eckes no funciona con números superiores a 127 porque no tiene en cuenta que los bytes se vuelven negativos a causa de 127 Están firmados.
Big_Bad_E
2

Agregaré otra respuesta que sea la más rápida posible ׂ (sí, incluso más que la respuesta aceptada), PERO no funcionará en todos los casos. SIN EMBARGO, funcionará para cada escenario concebible:

Simplemente puede usar String como intermedio. Tenga en cuenta que esto le dará el resultado correcto a pesar de que parece que el uso de String podría producir resultados incorrectos MIENTRAS SABE QUE ESTÁ TRABAJANDO CON STRINGS "NORMALES". Este es un método para aumentar la efectividad y simplificar el código, que a cambio debe usar algunas suposiciones en las cadenas de datos en las que opera.

Contras de usar este método: si está trabajando con algunos caracteres ASCII como estos símbolos al comienzo de la tabla ASCII, las siguientes líneas pueden fallar, pero seamos sinceros, probablemente nunca las usará de todos modos.

Pro de usar este método: recuerde que la mayoría de las personas generalmente trabajan con algunas cadenas normales sin caracteres inusuales y luego el método es la forma más simple y rápida de hacerlo.

de Long a byte []:

byte[] arr = String.valueOf(longVar).getBytes();

del byte [] a Long:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Yonatan Nir
fuente
2
Perdón por la necroposición, pero eso está mal. P.ej. String.valueOf (0) .getBytes () [0] == 0x30. ¡Sorpresa! La cadena # getBytes devolverá símbolos de dígitos codificados en ASCII, no dígitos: '0'! = 0, sino '0' == 0x30
Kirill Gamazkov
Bueno, tal vez si hubieras leído toda mi respuesta, entonces habrías visto que me lo había advertido en la respuesta misma ...
Yonatan Nir
1
Ah, no entendí que los datos de bytes intermedios [] se tratan como (casi) opacos. Tu truco servirá para este escenario.
Kirill Gamazkov
1

Si ya está utilizando un OutputStreampara escribir en el socket, DataOutputStream podría ser una buena opción. Aquí hay un ejemplo:

// Assumes you are currently working with a SocketOutputStream.

SocketOutputStream outputStream = ...
long longValue = ...

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

dataOutputStream.writeLong(longValue);
dataOutputStream.flush();

Hay métodos similares para short, int, float, etc. A continuación, puede utilizar DataInputStream en el lado receptor.

Matt Solnit
fuente
0

Aquí hay otra forma de convertir byte[]a longJava 8 o más reciente:

private static int bytesToInt(final byte[] bytes, final int offset) {
    assert offset + Integer.BYTES <= bytes.length;

    return (bytes[offset + Integer.BYTES - 1] & 0xFF) |
            (bytes[offset + Integer.BYTES - 2] & 0xFF) << Byte.SIZE |
            (bytes[offset + Integer.BYTES - 3] & 0xFF) << Byte.SIZE * 2 |
            (bytes[offset + Integer.BYTES - 4] & 0xFF) << Byte.SIZE * 3;
}

private static long bytesToLong(final byte[] bytes, final int offset) {
    return toUnsignedLong(bytesToInt(bytes, offset)) << Integer.SIZE |
            toUnsignedLong(bytesToInt(bytes, offset + Integer.BYTES));
}

La conversión de a longpuede expresarse como los bits de orden alto y bajo de dos valores enteros sujetos a un OR por bits. Tenga en cuenta que el toUnsignedLonges de la Integerclase y la primera llamada a toUnsignedLongpuede ser superflua.

La conversión opuesta también se puede desenrollar, como han mencionado otros.

Dave Jarvis
fuente
0

Extensiones Kotlin para tipos Long y ByteArray:

fun Long.toByteArray() = numberToByteArray(Long.SIZE_BYTES) { putLong(this@toByteArray) }

private inline fun numberToByteArray(size: Int, bufferFun: ByteBuffer.() -> ByteBuffer): ByteArray =
    ByteBuffer.allocate(size).bufferFun().array()

@Throws(NumberFormatException::class)
fun ByteArray.toLong(): Long = toNumeric(Long.SIZE_BYTES) { long }

@Throws(NumberFormatException::class)
private inline fun <reified T: Number> ByteArray.toNumeric(size: Int, bufferFun: ByteBuffer.() -> T): T {
    if (this.size != size) throw NumberFormatException("${T::class.java.simpleName} value must contains $size bytes")

    return ByteBuffer.wrap(this).bufferFun()
}

Puede ver el código completo en mi biblioteca https://github.com/ArtemBotnev/low-level-extensions

Artem Botnev
fuente