Wie konvertiere ich Long in Java in Byte [] und zurück?

159

Wie konvertiere ich ein longin ein byte[]und zurück in Java?

Ich versuche, a longin a umzuwandeln , byte[]damit ich das byte[]über eine TCP-Verbindung senden kann . Auf der anderen Seite möchte ich das nehmen byte[]und es wieder in ein konvertieren double.

Emre801
quelle
Eine weitere Alternative ist Maps.transformValues, ein allgemeines Tool zum Konvertieren von Sammlungen. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul
1
Siehe auch stackoverflow.com/q/27559449/32453, wenn Sie ein Long in die geringste Anzahl von Base64-Zeichen konvertieren möchten .
Rogerdpack
Vielleicht sollte betont werden, dass die Konvertierungspipeline 'long -> byte [] -> double' ist, nicht 'long -> byte [] -> long -> double'.
Kirill Gamazkov

Antworten:

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

Oder in eine Klasse eingeschlossen, um zu vermeiden, dass ByteBuffers wiederholt erstellt werden:

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

Da dies immer beliebter wird, möchte ich nur erwähnen, dass Sie in den allermeisten Fällen besser dran sind, eine Bibliothek wie Guava zu verwenden. Und wenn Sie einen merkwürdigen Widerstand gegen Bibliotheken haben, sollten Sie diese Antwort wahrscheinlich zuerst für native Java-Lösungen in Betracht ziehen . Ich denke, die Hauptsache, die meine Antwort wirklich hat, ist, dass Sie sich nicht selbst um die Endianz des Systems sorgen müssen.

Brad Mace
quelle
3
Clever ... aber Sie erstellen und verwerfen für jede Konvertierung einen temporären ByteBuffer. Nicht gut, wenn Sie mehrere Longs pro Nachricht und / oder viele Nachrichten senden.
Stephen C
1
@Stephen - Ich habe gerade genug getan, um die Verwendung von ByteBuffer zu demonstrieren, aber ich habe ein Beispiel für die Verwendung in einer Utility-Klasse hinzugefügt.
Brad Mace
8
Ich denke, das bytesToLong () hier würde fehlschlagen, da sich die Position nach dem Put am Ende des Puffers befindet, nicht am Anfang. Ich denke, Sie würden eine Puffer-Unterlauf-Ausnahme bekommen.
Alex Miller
11
Vor Java 8 können Sie Long.SIZE / Byte.SIZE anstelle von Long.BYTES verwenden, um eine magische Zahl zu vermeiden.
Jvdbogae
8
Die Wiederverwendung dieses Bytepuffers ist äußerst problematisch, nicht nur aus Gründen der Thread-Sicherheit, wie andere kommentierten. Dazwischen würde nicht nur ein '.clear ()' benötigt, sondern es ist auch nicht offensichtlich, dass der Aufruf von .array () im ByteBuffer das Backing-Array gegenüber einer Kopie zurückgibt. Wenn Sie also wiederholt aufrufen und an den anderen Ergebnissen festhalten, handelt es sich tatsächlich um dasselbe Array, das die vorherigen Werte wiederholt überschreibt. Der Hadoop-Link in einem Kommentar unten ist der leistungsstärkste und vermeidet jedes dieser Probleme.
Aaron Zinman
84

Ich habe die ByteBuffer-Methode gegen einfache bitweise Operationen getestet, aber letztere ist deutlich schneller.

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
quelle
1
Anstelle von Ergebnis | = (b [i] & 0xFF); Wir könnten einfach result | = b [i] verwenden; as und mit 0xFF für ein bisschen ändert nichts.
Vipul
3
@Vipul Das bitweise-und spielt eine Rolle, da nur result |= b[i]der Byte-Wert zuerst in long konvertiert wird, was eine Vorzeichenerweiterung bedeutet. Ein Byte mit dem Wert -128 (hex 0x80) wird zu einem Long mit dem Wert -128 (hex 0xFFFF FFFF FFFF FF80). Zuerst nach der Konvertierung werden die Werte oder: ed zusammen. Die Verwendung von bitwise-and schützt davor, indem zuerst das Byte in ein int konvertiert und die Vorzeichenerweiterung abgeschnitten wird : (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Warum Bytes in Java signiert sind, ist mir ein Rätsel, aber ich denke, dass es zu anderen Typen passt.
Brainstorming
@Brainstorm Ich habe den Fall -128 mit | = b [i] und mit | = (b [i] & 0xFF) ausprobiert und die Ergebnisse sind die gleichen !!
Mahmoud Hanafy
Das Problem ist, dass das Byte vor dem Anwenden der Verschiebung heraufgestuft wird, was seltsame Probleme mit dem Vorzeichenbit verursacht. Deshalb zuerst und (&) mit 0xFF, um den richtigen Wert zum Verschieben zu erhalten.
Wytze
Ich versuche, diese Daten (Daten = neues Byte [] {(Byte) 0xDB, (Byte) 0xA7, 0x53, (Byte) 0xF8, (Byte) 0xA8, 0x0C, 0x66, 0x8};) in long umzuwandeln, aber sie kehren zurück falscher Wert -2619032330856274424, der erwartete Wert ist 989231983928329832
jefry jacky
29

Wenn Sie nach einer schnellen, nicht abgerollten Version suchen, sollte dies den Trick tun, vorausgesetzt, ein Byte-Array namens "b" mit einer Länge von 8:

Byte [] -> lang

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 [] als genaues Gegenstück zu den oben genannten

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
quelle
1
Danke, das Beste!
Miha_x64
15

Warum brauchst du das Byte []? warum nicht einfach in die Steckdose schreiben?

Ich nehme an, Sie bedeuten lange nicht lange , letztere Bedarf für Nullwerte zu ermöglichen.

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
quelle
8
Er fragte, wie Sie in Byte [] und zurück konvertieren. Gute Antwort, aber die Frage nicht beantwortet. Sie fragen, warum, weil Sie annehmen, dass es unnötig ist, aber das ist eine falsche Annahme. Was ist, wenn er sprachübergreifend oder plattformübergreifend arbeitet? DataOutputStream hilft Ihnen dort nicht weiter.
user1132959
4
Wenn er sprach- oder plattformübergreifend arbeitet, ist es wichtig, die Bytes in einer bekannten Reihenfolge zu senden. Diese Methode macht das (sie schreibt sie "High Byte zuerst") gemäß den Dokumenten. Die akzeptierte Antwort nicht (sie schreibt sie in der "aktuellen Reihenfolge" gemäß den Dokumenten). Die Frage besagt, dass er sie über eine TCP-Verbindung senden möchte. Das byte[]ist nur ein Mittel zu diesem Zweck.
Ian McLaird
3
@IanMcLaird Die akzeptierte Antwort verwendet eine bekannte Reihenfolge. Es erstellt eine neue, ByteBufferdie laut den Dokumenten "Die anfängliche Reihenfolge eines Byte-Puffers ist immer BIG_ENDIAN.
David Phillips
4

Schreiben Sie einfach den Long in einen DataOutputStream mit einem zugrunde liegenden ByteArrayOutputStream . Aus dem ByteArrayOutputStream können Sie das Byte-Array über toByteArray () abrufen :

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

Funktioniert entsprechend für andere Grundelemente.

Hinweis: Für TCP benötigen Sie das Byte [] nicht manuell. Sie verwenden einen Socket socket und seine Streams

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

stattdessen.

Michael Konietzka
quelle
4

Sie können die Implementierung in org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html verwenden

Der Quellcode ist hier:

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

Suchen Sie nach den Methoden toLong und toBytes.

Ich glaube, mit der Softwarelizenz können Sie Teile des Codes nehmen und verwenden, aber bitte überprüfen Sie dies.

Marquez
quelle
Warum verwendet diese Implementierung XOR (^ =) anstelle von OR? github.com/apache/hbase/blob/master/hbase-common/src/main/java/…
Opfer
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
quelle
2
Sie können die ArrayList überspringen: public static byte [] longToBytes (long l) {long num = l; Byte [] Byte = neues Byte [8]; für (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); num >> = 8; } return bytesp; }
eckes
Die ursprüngliche Methode funktioniert nicht mit negativen Zahlen, da sie sich in einer Endlosschleife befindet, während (l! = 0). Die Methode von @ eckes funktioniert nicht mit Zahlen über 127, da er nicht berücksichtigt, dass Bytes über 127 negativ werden sie sind unterschrieben.
Big_Bad_E
2

Ich werde eine weitere Antwort hinzufügen, die die schnellstmögliche ist yes (ja, sogar mehr als die akzeptierte Antwort), ABER es wird nicht für jeden einzelnen Fall funktionieren. Es wird jedoch für jedes denkbare Szenario funktionieren:

Sie können String einfach als Zwischenstufe verwenden. Beachten Sie, dass Sie damit das richtige Ergebnis erhalten, auch wenn die Verwendung von String möglicherweise zu falschen Ergebnissen führt, solange Sie wissen, dass Sie mit "normalen" Zeichenfolgen arbeiten. Dies ist eine Methode, um die Effektivität zu erhöhen und den Code zu vereinfachen. Im Gegenzug müssen einige Annahmen für die Datenzeichenfolgen verwendet werden, mit denen er arbeitet.

Nachteile der Verwendung dieser Methode: Wenn Sie mit einigen ASCII-Zeichen wie diesen Symbolen am Anfang der ASCII-Tabelle arbeiten, schlagen die folgenden Zeilen möglicherweise fehl, aber seien wir ehrlich - Sie werden sie wahrscheinlich sowieso nie verwenden.

Vorteile der Verwendung dieser Methode: Denken Sie daran, dass die meisten Benutzer normalerweise mit normalen Zeichenfolgen ohne ungewöhnliche Zeichen arbeiten. Dann ist die Methode der einfachste und schnellste Weg.

von Long bis Byte []:

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

von Byte [] bis Long:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Yonatan Nir
quelle
2
Entschuldigung für die Nekropostierung, aber das ist einfach falsch. Z.B. String.valueOf (0) .getBytes () [0] == 0x30. Überraschung! String # getBytes gibt ASCII-codierte Ziffernsymbole zurück, keine Ziffern: '0'! = 0, aber '0' == 0x30
Kirill Gamazkov
Nun, wenn Sie meine gesamte Antwort gelesen hätten, hätten Sie gesehen, dass ich in der Antwort selbst davor gewarnt habe.
Yonatan Nir
1
Ah, ich habe den Punkt übersehen, dass Zwischenbyte [] -Daten als (fast) undurchsichtig behandelt werden. Ihr Trick reicht für dieses Szenario.
Kirill Gamazkov
1

Wenn Sie bereits ein OutputStreamzum Schreiben in den Socket verwenden, ist DataOutputStream möglicherweise eine gute Lösung . Hier ist ein Beispiel:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

Es gibt ähnliche Methoden für short, int, floatusw. Sie können dann mit Datainputstream auf der Empfangsseite.

Matt Solnit
quelle
0

Hier ist eine andere Möglichkeit, byte[]auf longJava 8 oder höher zu konvertieren :

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

Das Konvertieren von a longkann als Bits hoher und niedriger Ordnung von zwei ganzzahligen Werten ausgedrückt werden, die einem bitweisen ODER unterliegen. Beachten Sie, dass das toUnsignedLongaus der IntegerKlasse stammt und der erste Aufruf toUnsignedLongmöglicherweise überflüssig ist.

Die entgegengesetzte Konvertierung kann ebenfalls abgewickelt werden, wie andere bereits erwähnt haben.

Dave Jarvis
quelle
0

Kotlin-Erweiterungen für Long- und ByteArray-Typen:

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

Den vollständigen Code finden Sie in meiner Bibliothek unter https://github.com/ArtemBotnev/low-level-extensions

Artem Botnev
quelle