Jak przekonwertować Long na bajt [] iz powrotem w java

159

Jak przekonwertować a longna a byte[]iz powrotem w Javie?

Próbuję przekonwertować a longna, byte[]aby móc wysłać byte[]połączenie TCP. Z drugiej strony chcę to wziąć byte[]i przekształcić z powrotem w plik double.

Emre801
źródło
Inną alternatywą będzie Maps.transformValues, ogólne narzędzie do konwersji kolekcji. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul,
1
Zobacz także stackoverflow.com/q/27559449/32453, jeśli Twoim celem jest przekonwertowanie długiego na jak najmniejszą liczbę znaków Base64.
rogerdpack
Może warto podkreślić, że potok konwersji to „long -> byte [] -> double”, a nie „long -> byte [] -> long -> double”.
Kirill Gamazkov

Odpowiedzi:

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

Lub opakowane w klasę, aby uniknąć wielokrotnego tworzenia 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();
    }
}

Ponieważ staje się to tak popularne, chcę tylko wspomnieć, że myślę, że w większości przypadków lepiej jest korzystać z biblioteki takiej jak Guava. A jeśli masz jakiś dziwny sprzeciw wobec bibliotek, prawdopodobnie powinieneś najpierw rozważyć tę odpowiedź w przypadku natywnych rozwiązań Java. Myślę, że najważniejszą rzeczą, jaką naprawdę ma moja odpowiedź, jest to, że nie musisz sam martwić się o endian -ness systemu.

Brad Mace
źródło
3
Sprytne ... ale tworzysz i odrzucasz tymczasowy ByteBuffer dla każdej konwersji. Niedobrze, jeśli wysyłasz wiele długich wiadomości na wiadomość i / lub wiele wiadomości.
Stephen C
1
@Stephen - robiłem wystarczająco dużo, aby zademonstrować, jak używać ByteBuffer, ale poszedłem dalej i dodałem przykład użycia go w klasie użytkowej.
Brad Mace
8
Myślę, że bytesToLong () tutaj nie powiodłoby się, ponieważ pozycja po wprowadzeniu znajduje się na końcu bufora, a nie na początku. Myślę, że dostaniesz wyjątek niedopełnienia bufora.
Alex Miller
11
Pre-Java 8, możesz użyć Long.SIZE / Byte.SIZE zamiast Long.BYTES, aby uniknąć magicznej liczby.
jvdbogae,
8
Ponowne użycie tego bufora bajtów jest wysoce problematyczne i to nie tylko ze względów bezpieczeństwa wątków, jak komentowali inni. W międzyczasie potrzebna byłaby nie tylko '.clear ()', ale nie jest oczywiste, że wywołanie .array () w ByteBuffer zwraca tablicę zapasową w porównaniu z kopią. Tak więc, jeśli wywołujesz wielokrotnie i zatrzymujesz inne wyniki, w rzeczywistości są one tą samą tablicą, która wielokrotnie nadpisuje poprzednie wartości. Link hadoop w komentarzu poniżej jest najbardziej wydajny i pozwala uniknąć tych problemów.
Aaron Zinman,
84

Przetestowałem metodę ByteBuffer na zwykłych operacjach bitowych, ale ta ostatnia jest znacznie szybsza.

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
źródło
1
Zamiast wyniku | = (b [i] & 0xFF); Moglibyśmy po prostu użyć wyniku | = b [i]; jak iz 0xFF przez chwilę nic nie modyfikuje.
Vipul
3
@Vipul Bitowe-i ma znaczenie, ponieważ przy wykonywaniu tylko result |= b[i]bajtów wartość zostanie najpierw przekonwertowana na long, co powoduje rozszerzenie znaku. Bajt o wartości -128 (szesnastkowo 0x80) zamieni się w długi o wartości -128 (szesnastkowo 0xFFFF FFFF FFFF FF80). Najpierw po przeliczeniu są wartości lub: ed razem. Korzystanie bitowe, i chroni przed tym przekształcając najpierw bajt do int i odcięcie przedłużenia rejestracji: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Dlaczego bajty są podpisywane w javie jest dla mnie trochę tajemnicą, ale myślę, że pasuje do innych typów.
Burza mózgów
@ Burza mózgów Wypróbowałem przypadek -128 z | = b [i] iz | = (b [i] i 0xFF), a wyniki są takie same !!
Mahmoud Hanafy
Problem polega na tym, że bajt jest promowany przed zastosowaniem przesunięcia, co powoduje dziwne problemy z bitem znaku. Dlatego najpierw i (&) to z 0xFF, aby uzyskać poprawną wartość do przesunięcia.
Wytze
Próbuję przekonwertować te dane (dane = nowy bajt [] {(bajt) 0xDB, (bajt) 0xA7, 0x53, (bajt) 0xF8, (bajt) 0xA8, 0x0C, 0x66, 0x8};) na długie, ale zwracają wartość fałszywa -2619032330856274424, oczekiwana wartość to 989231983928329832
jefry jacky
29

Jeśli szukasz szybkiej wersji rozwiniętej, powinno to załatwić sprawę, zakładając tablicę bajtów o nazwie „b” o długości 8:

bajt [] -> długi

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 [] jako dokładny odpowiednik powyższego

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
źródło
1
Dziękuję, najlepsze!
Miha_x64
15

Dlaczego potrzebujesz bajtu []? dlaczego po prostu nie zapisać go do gniazda?

Zakładam, że masz na myśli raczej long niż Long , ta ostatnia musi dopuszczać wartości zerowe.

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
źródło
8
Zapytał, jak konwertujesz na bajt [] iz powrotem. Dobra odpowiedź, ale nie odpowiedział na pytanie. Pytasz dlaczego, ponieważ zakładasz, że jest to niepotrzebne, ale to błędne założenie. A co, jeśli robi to w różnych językach lub na różnych platformach? DataOutputStream Ci w tym nie pomoże.
user1132959
4
Jeśli robi to w wielu językach lub na wielu platformach, ważne jest wysyłanie bajtów w znanej kolejności. Ta metoda robi to (zapisuje je „najpierw starszy bajt”) zgodnie z dokumentacją. Przyjęta odpowiedź nie (zapisuje je w „bieżącej kolejności” zgodnie z dokumentacją). Pytanie mówi, że chce je wysłać przez połączenie TCP. To byte[]tylko środek do tego celu.
Ian McLaird
3
@IanMcLaird Zaakceptowana odpowiedź używa znanej kolejności. Tworzy nowy, ByteBufferktóry zgodnie z dokumentacją „Początkowa kolejność bufora bajtów jest zawsze BIG_ENDIAN.
David Phillips
4

Po prostu zapisz long do DataOutputStream z bazowym ByteArrayOutputStream . Z ByteArrayOutputStream można pobrać tablicę bajtów poprzez 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));       
        }
}

Działa odpowiednio dla innych prymitywów.

Wskazówka: w przypadku protokołu TCP bajt [] nie jest potrzebny ręcznie. Będziesz używać gniazda socket i jego strumieni

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

zamiast.

Michael Konietzka
źródło
4

Możesz użyć implementacji w org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

Kod źródłowy jest tutaj:

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

Poszukaj metod toLong i toBytes.

Uważam, że licencja na oprogramowanie pozwala na pobranie części kodu i użycie go, ale prosimy o sprawdzenie tego.

Marquez
źródło
Dlaczego ta implementacja używa XOR (^ =) zamiast OR? github.com/apache/hbase/blob/master/hbase-common/src/main/java/…
virtutim
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
źródło
2
możesz pominąć ArrayList: public static byte [] longToBytes (long l) {long num = l; bajt [] bajty = nowy bajt [8]; for (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); num >> = 8; } return bytesp; }
eckes
Oryginalna metoda nie działa z liczbami ujemnymi, ponieważ wchodzi w nieskończoną pętlę w while (l! = 0), metoda @ eckes nie działa z liczbami powyżej 127, ponieważ nie uwzględnia bajtów, które mają wartość ujemną powyżej 127, ponieważ są podpisane.
Big_Bad_E
2

Dodam kolejną odpowiedź, która jest najszybsza z możliwych ׂ (tak, nawet więcej niż zaakceptowana odpowiedź), ALE nie zadziała w każdym przypadku. JEDNAK będzie działać w każdym możliwym scenariuszu:

Możesz po prostu użyć String jako pośredniego. Zauważ, że da to poprawny wynik, nawet jeśli wydaje się, że użycie Ciągu może dać błędne wyniki, TAK DŁUGO, JAK WIESZ, ŻE PRACUJESZ Z „NORMALNYMI” STRUNIAMI. Jest to metoda zwiększania efektywności i uproszczenia kodu, która w zamian musi wykorzystywać pewne założenia dotyczące ciągów danych, na których operuje.

Wada korzystania z tej metody: Jeśli pracujesz z niektórymi znakami ASCII, takimi jak te symbole na początku tabeli ASCII, poniższe wiersze mogą się nie powieść, ale spójrzmy prawdzie w oczy - prawdopodobnie i tak nigdy ich nie użyjesz.

Zaleta tej metody: pamiętaj, że większość ludzi zwykle pracuje z normalnymi ciągami znaków bez żadnych niezwykłych znaków, a wtedy metoda jest najprostsza i najszybsza.

od Long do Byte []:

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

od bajtu [] do długości:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Yonatan Nir
źródło
2
Przepraszamy za nekroposty, ale to po prostu błędne. Np. String.valueOf (0) .getBytes () [0] == 0x30. Niespodzianka! Ciąg # getBytes zwróci symbole cyfr zakodowane w ASCII, a nie cyfry: „0”! = 0, ale „0” == 0x30
Kirill Gamazkov
Cóż, może gdybyś przeczytał całą moją odpowiedź, to zobaczyłbyś, że ostrzegałem o tym w samej odpowiedzi ..
Yonatan Nir
1
Ach, przegapiłem punkt, w którym pośrednie dane bajtu [] są traktowane jako (prawie) nieprzejrzyste. Twoja sztuczka wystarczy w tym scenariuszu.
Kirill Gamazkov
1

Jeśli już używasz an OutputStreamdo zapisu w gnieździe, DataOutputStream może być dobrym rozwiązaniem. Oto przykład:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

Istnieją podobne metody short, int, float, itd. Następnie można użyć DataInputStream po stronie odbiorczej.

Matt Solnit
źródło
0

Oto inny sposób konwersji byte[]na longjęzyk Java 8 lub nowszy:

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

Konwersja a longmoże być wyrażona jako bity najwyższego i najniższego rzędu dwóch wartości całkowitych z zastrzeżeniem bitowego OR. Zauważ, że toUnsignedLongjest z Integerklasy i pierwsze wywołanie toUnsignedLongmoże być zbędne.

Można również rozwinąć konwersję odwrotną, o czym wspominali inni.

Dave Jarvis
źródło
0

Rozszerzenia Kotlin dla typów Long i 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()
}

Możesz zobaczyć pełny kod w mojej bibliotece https://github.com/ArtemBotnev/low-level-extensions

Artem Botnev
źródło