Comment puis-je convertir Long en octet [] et revenir en Java

159

Comment puis-je convertir un longen a byte[]et inversement en Java?

J'essaye de convertir un longen unbyte[] pour que je puisse envoyer le byte[]via une connexion TCP. De l'autre côté, je veux prendre cela byte[]et le reconvertir en un fichier double.

Emre801
la source
Une autre alternative sera Maps.transformValues, un outil général de conversion de collections. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul
1
Voir aussi stackoverflow.com/q/27559449/32453 si votre objectif est de convertir un long en le plus petit nombre de caractères Base64.
rogerdpack
Il faut peut-être souligner que le pipeline de conversion est «long -> byte [] -> double», et non «long -> byte [] -> long -> double».
Kirill Gamazkov

Réponses:

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();
}

Ou enveloppé dans une classe pour éviter de créer à plusieurs reprises des 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();
    }
}

Étant donné que cela devient si populaire, je veux juste mentionner que je pense que vous feriez mieux d'utiliser une bibliothèque comme Guava dans la grande majorité des cas. Et si vous avez une étrange opposition aux bibliothèques, vous devriez probablement considérer cette réponse en premier pour les solutions Java natives. Je pense que l'essentiel de ma réponse est que vous n'avez pas à vous soucier vous-même de la finalité du système.

Brad Mace
la source
3
Intelligent ... mais vous créez et supprimez un ByteBuffer temporaire pour chaque conversion. Pas bon si vous envoyez plusieurs longs par message et / ou beaucoup de messages.
Stephen C
1
@Stephen - J'en faisais juste assez pour montrer comment utiliser ByteBuffer, mais je suis allé de l'avant et j'ai ajouté un exemple d'utilisation dans une classe utilitaire.
Brad Mace
8
Je pense que bytesToLong () échouerait ici car la position après la mise est à la fin du tampon, pas au début. Je pense que vous obtiendrez une exception de dépassement de la mémoire tampon.
Alex Miller le
11
Avant Java 8, vous pouvez utiliser Long.SIZE / Byte.SIZE au lieu de Long.BYTES pour éviter un nombre magique.
jvdbogae
8
La réutilisation de ce bytebuffer est très problématique, et pas seulement pour des raisons de sécurité des threads comme d'autres l'ont commenté. Non seulement un '.clear ()' serait nécessaire entre les deux, mais ce qui n'est pas évident, c'est que l'appel de .array () sur le ByteBuffer renvoie le tableau de sauvegarde par rapport à une copie. Ainsi, si vous appelez à plusieurs reprises et maintenez les autres résultats, ils sont en fait tous le même tableau qui écrase à plusieurs reprises les valeurs précédentes. Le lien hadoop dans un commentaire ci-dessous est le plus performant et évite l'un de ces problèmes.
Aaron Zinman
84

J'ai testé la méthode ByteBuffer contre des opérations binaires simples, mais cette dernière est nettement plus rapide.

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
la source
1
Au lieu du résultat | = (b [i] & 0xFF); Nous pourrions simplement utiliser result | = b [i]; comme et avec 0xFF pendant un peu ne modifie rien.
Vipul
3
@Vipul Le bit-et est important parce que lorsque vous faites juste result |= b[i]la valeur d'octet sera d'abord convertie en long qui fait l'extension de signe. Un octet avec la valeur -128 (hex 0x80) se transforme en un long avec la valeur -128 (hex 0xFFFF FFFF FFFF FF80). Tout d'abord après la conversion sont les valeurs ou: ed ensemble. L' utilisation et protège bitwise contre cela en convertissant d' abord l'octet à un int et en coupant l'extension de signe: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Pourquoi les octets sont signés en java est un peu un mystère pour moi, mais je suppose que cela convient à d'autres types.
Brainstorm
@Brainstorm J'ai essayé le cas -128 avec | = b [i] et avec | = (b [i] & 0xFF) et les résultats sont les mêmes !!
Mahmoud Hanafy
Le problème est que l'octet est promu avant que le décalage ne soit appliqué, ce qui provoque d'étranges problèmes avec le bit de signe. Par conséquent, nous d'abord et (&) avec 0xFF pour obtenir la valeur correcte à décaler.
Wytze le
J'essaye de convertir ces données (data = new byte [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8};) en long, mais elles retournent fausse valeur -2619032330856274424, la valeur attendue est 989231983928329832
jefry jacky
29

Si vous recherchez une version rapide déroulée, cela devrait faire l'affaire, en supposant un tableau d'octets appelé "b" avec une longueur de 8:

octet [] -> long

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);

long -> byte [] comme contrepartie exacte de ce qui précède

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
la source
1
Merci, le meilleur!
Miha_x64
15

Pourquoi avez-vous besoin de l'octet []? pourquoi ne pas simplement l'écrire sur la socket?

Je suppose que vous voulez dire long plutôt que Long , ce dernier doit permettre des valeurs nulles.

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
la source
8
Il a demandé comment vous convertissez en octet [] et inversement. Bonne réponse mais n'a pas répondu à la question. Vous demandez pourquoi parce que vous supposez que ce n'est pas nécessaire, mais que c'est une hypothèse erronée. Et s'il fait du cross-language ou cross-platform? DataOutputStream ne vous aidera pas là-bas.
user1132959
4
S'il utilise plusieurs langues ou plates-formes, il est important d'envoyer les octets dans un ordre connu. Cette méthode fait cela (elle les écrit "octet de poids fort en premier") selon la documentation. La réponse acceptée ne le fait pas (elle les écrit dans «l'ordre courant» selon la documentation). La question indique qu'il souhaite les envoyer via une connexion TCP. Le byte[]est juste un moyen à cette fin.
Ian McLaird
3
@IanMcLaird La réponse acceptée utilise un ordre connu. Il crée un nouveau ByteBufferqui selon la documentation "L'ordre initial d'un tampon d'octets est toujours BIG_ENDIAN.
David Phillips
4

Écrivez simplement le long dans un DataOutputStream avec un ByteArrayOutputStream sous-jacent . Depuis ByteArrayOutputStream, vous pouvez obtenir le tableau d'octets via 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));       
        }
}

Fonctionne pour les autres primitives en conséquence.

Astuce: pour TCP, vous n'avez pas besoin de l'octet [] manuellement. Vous utiliserez un Socket socket et ses flux

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

au lieu.

Michael Konietzka
la source
4

Vous pouvez utiliser l'implémentation dans org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

Le code source est ici:

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# Octets.toBytes% 28long% 29

Recherchez les méthodes toLong et toBytes.

Je pense que la licence du logiciel vous permet de prendre des parties du code et de l'utiliser, mais veuillez le vérifier.

Marquez
la source
Pourquoi cette implémentation utilise-t-elle XOR (^ =) au lieu 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
la source
2
vous pouvez sauter la ArrayList: public static byte [] longToBytes (long l) {long num = l; octet [] octets = nouvel octet [8]; for (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); num >> = 8; } return bytesp; }
eckes
La méthode originale ne fonctionne pas avec des nombres négatifs car elle obtient une boucle infinie dans while (l! = 0), la méthode de @ eckes ne fonctionne pas avec des nombres supérieurs à 127 car il ne tient pas compte des octets devenant négatifs sur 127 cause ils sont signés.
Big_Bad_E
2

J'ajouterai une autre réponse qui est la plus rapide possible ׂ (oui, encore plus que la réponse acceptée), MAIS cela ne fonctionnera pas pour chaque cas. CEPENDANT, cela fonctionnera pour tous les scénarios imaginables:

Vous pouvez simplement utiliser String comme intermédiaire. Notez que cela vous donnera le résultat correct même s'il semble que l'utilisation de String puisse donner des résultats incorrects AUSSI QUE VOUS SAVEZ QUE VOUS TRAVAILLEZ AVEC DES CHAÎNES "NORMALES". C'est une méthode pour augmenter l'efficacité et rendre le code plus simple qui en retour doit utiliser certaines hypothèses sur les chaînes de données sur lesquelles il opère.

Contre d'utiliser cette méthode: si vous travaillez avec des caractères ASCII comme ces symboles au début de la table ASCII, les lignes suivantes peuvent échouer, mais avouons-le - vous ne les utiliserez probablement jamais de toute façon.

Pro de l'utilisation de cette méthode: rappelez-vous que la plupart des gens travaillent généralement avec des chaînes normales sans aucun caractère inhabituel et que la méthode est le moyen le plus simple et le plus rapide.

de Long à byte []:

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

de l'octet [] à Long:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Yonatan Nir
la source
2
Désolé pour le nécropostage, mais c'est tout simplement faux. Par exemple. String.valueOf (0) .getBytes () [0] == 0x30. Surprise! La chaîne # getBytes renverra des symboles numériques encodés en ASCII, pas des chiffres: '0'! = 0, mais '0' == 0x30
Kirill Gamazkov
Eh bien, peut-être que si vous aviez lu toute ma réponse, vous auriez vu que je l'ai averti dans la réponse elle-même ..
Yonatan Nir
1
Ah, j'ai manqué le point que les données d'octet intermédiaire [] sont traitées comme (presque) opaques. Votre astuce fera l'affaire pour ce scénario.
Kirill Gamazkov
1

Si vous utilisez déjà un OutputStreampour écrire sur le socket, DataOutputStream peut être un bon choix . Voici un exemple:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

Il existe des méthodes similaires pour short, int, float, etc. Vous pouvez ensuite utiliser DataInputStream du côté de réception.

Matt Solnit
la source
0

Voici une autre façon de passer byte[]à l' longutilisation de Java 8 ou plus récent:

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 conversion de a longpeut être exprimée comme les bits de poids fort et de poids faible de deux valeurs entières soumises à un OU au niveau du bit. Notez que le toUnsignedLongest de la Integerclasse et le premier appel à toUnsignedLongpeut être superflu.

La conversion inverse peut également être déroulée, comme d'autres l'ont mentionné.

Dave Jarvis
la source
0

Extensions Kotlin pour les types Long et 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()
}

Vous pouvez voir le code complet dans ma bibliothèque https://github.com/ArtemBotnev/low-level-extensions

Artem Botnev
la source