Bagaimana cara mengkonversi Long ke byte [] dan kembali di java

159

Bagaimana cara mengonversi a longke byte[]dan kembali di Jawa?

Saya mencoba mengonversikan longke byte[]sehingga saya dapat mengirim byte[]melalui koneksi TCP. Di sisi lain saya ingin mengambil byte[]dan mengubahnya kembali menjadi double.

Emre801
sumber
Alternatif lain adalah Maps.transformValues, alat umum untuk mengonversi koleksi. docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
Raul
1
Lihat juga stackoverflow.com/q/27559449/32453 jika tujuan Anda adalah mengubah panjang menjadi jumlah karakter Base64 yang paling sedikit.
rogerdpack
Mungkin perlu ditekankan bahwa pipa konversi adalah 'long -> byte [] -> double', bukan 'long -> byte [] -> long -> double'.
Kirill Gamazkov

Jawaban:

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

Atau terbungkus kelas untuk menghindari berulang kali membuat 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();
    }
}

Karena ini menjadi sangat populer, saya hanya ingin menyebutkan bahwa saya pikir Anda lebih baik menggunakan perpustakaan seperti Guava di sebagian besar kasus. Dan jika Anda memiliki beberapa oposisi aneh terhadap perpustakaan, Anda mungkin harus mempertimbangkan jawaban ini terlebih dahulu untuk solusi java asli. Saya pikir hal utama jawaban saya benar-benar telah berjalan untuk itu adalah bahwa Anda tidak perlu khawatir tentang ke-endian-an dari sistem sendiri.

Brad Mace
sumber
3
Pintar ... tetapi Anda membuat dan membuang ByteBuffer sementara untuk setiap konversi. Tidak bagus jika Anda mengirim beberapa pesan per pesan dan / atau banyak pesan.
Stephen C
1
@Stephen - Saya hanya melakukan cukup banyak untuk menunjukkan cara menggunakan ByteBuffer, tapi saya teruskan dan menambahkan contoh menggunakannya di kelas utilitas.
Brad Mace
8
Saya pikir bytesToLong () di sini akan gagal karena posisi setelah put berada di akhir buffer, bukan awal. Saya pikir Anda akan mendapatkan pengecualian buffer underflow.
Alex Miller
11
Sebelum Java 8, Anda dapat menggunakan Long.SIZE / Byte.SIZE alih-alih Long.BYTES untuk menghindari angka ajaib.
jvdbogae
8
Penggunaan kembali bytebuffer sangat bermasalah, dan tidak hanya untuk alasan keamanan thread seperti komentar orang lain. Tidak hanya '.clear ()' diperlukan di antara keduanya, tetapi yang tidak jelas adalah bahwa memanggil .array () pada ByteBuffer mengembalikan array dukungan versus salinan. Jadi, jika Anda menelepon berulang kali dan menahan hasil lainnya, mereka sebenarnya adalah array yang sama yang berulang kali menimpa nilai sebelumnya. Tautan hadoop dalam komentar di bawah adalah yang paling berkinerja dan menghindari salah satu dari masalah ini.
Aaron Zinman
84

Saya menguji metode ByteBuffer terhadap operasi bitwise biasa tetapi yang terakhir secara signifikan lebih cepat.

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
sumber
1
Alih-alih hasil | = (b [i] & 0xFF); Kita cukup menggunakan hasil | = b [i]; sebagai dan dengan 0xFF untuk sedikit tidak mengubah apa pun.
Vipul
3
@ Vulul Bitwise-dan tidak masalah karena ketika melakukan hanya result |= b[i]nilai byte pertama akan dikonversi menjadi panjang yang tidak menandatangani ekstensi. Byte dengan nilai -128 (hex 0x80) akan berubah menjadi panjang dengan nilai -128 (hex 0xFFFF FFFF FFFF FF80). Pertama setelah konversi adalah nilai atau: ed bersama. Menggunakan bitwise-dan melindungi terhadap ini dengan terlebih dahulu mengkonversi byte ke int dan potong ekstensi tanda: (byte)0x80 & 0xFF ==> (int)0xFFFF FF80 & 0xFF ==> (int) 0x80. Mengapa byte ditandatangani di java adalah sedikit misteri bagi saya, tapi saya kira itu cocok dengan tipe lainnya.
Brainstorm
@ Brainstorm saya mencoba case -128 dengan | = b [i] dan dengan | = (b [i] & 0xFF) dan hasilnya sama !!
Mahmoud Hanafy
Masalahnya adalah bahwa byte dipromosikan sebelum shift diterapkan yang menyebabkan masalah aneh dengan bit tanda. Karenanya kita pertama-tama dan (&) dengan 0xFF untuk mendapatkan nilai yang benar untuk bergeser.
Wytze
Saya mencoba untuk mengkonversi data ini (data = byte baru [] {(byte) 0xDB, (byte) 0xA7, 0x53, (byte) 0xF8, (byte) 0xA8, 0x0C, 0x66, 0x8};) menjadi panjang, tetapi kembali nilai salah -2619032330856274424, nilai yang diharapkan adalah 989231983928329832
jefry jacky
29

Jika Anda mencari versi yang belum dibuka, ini seharusnya bisa digunakan, dengan asumsi array byte bernama "b" dengan panjang 8:

byte [] -> panjang

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

panjang -> byte [] sebagai pasangan yang tepat untuk yang di atas

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
sumber
1
Terima kasih, yang terbaik!
Miha_x64
15

Mengapa Anda membutuhkan byte []? mengapa tidak menulis saja ke soket?

Saya berasumsi maksud Anda panjang daripada Panjang , yang terakhir perlu untuk memungkinkan nilai nol.

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
sumber
8
Dia bertanya bagaimana Anda mengkonversi ke byte [] dan kembali. Jawaban bagus tapi tidak menjawab pertanyaan. Anda bertanya mengapa karena Anda menganggap itu tidak perlu tetapi itu anggapan yang salah. Bagaimana jika dia melakukan lintas bahasa atau lintas platform? DataOutputStream tidak akan membantu Anda di sana.
user1132959
4
Jika dia melakukan lintas bahasa atau lintas platform, maka mengirim byte dalam urutan yang diketahui adalah penting. Metode ini melakukan itu (ia menulis "byte tinggi pertama") sesuai dengan dokumen. Jawaban yang diterima tidak (itu menuliskannya dalam "urutan saat ini" menurut dokumen). Pertanyaannya menyatakan bahwa ia ingin mengirim mereka melalui koneksi TCP. Itu byte[]hanyalah sarana untuk mencapai tujuan itu.
Ian McLaird
3
@IanMcLaird Jawaban yang diterima menggunakan perintah yang dikenal. Itu menciptakan yang baru ByteBufferyang menurut dokumen "Urutan awal buffer byte selalu BIG_ENDIAN.
David Phillips
4

Cukup tulis panjang ke DataOutputStream dengan ByteArrayOutputStream yang mendasarinya . Dari ByteArrayOutputStream Anda bisa mendapatkan byte-array 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));       
        }
}

Bekerja untuk primitif lainnya sesuai.

Petunjuk: Untuk TCP Anda tidak perlu byte [] secara manual. Anda akan menggunakan Socket socket dan alirannya

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

sebagai gantinya.

Michael Konietzka
sumber
4

Anda dapat menggunakan implementasinya di org.apache.hadoop.hbase.util.Bytes http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/util/Bytes.html

Kode sumber ada di sini:

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

Cari metode toLong dan toBytes.

Saya percaya lisensi perangkat lunak memungkinkan Anda untuk mengambil bagian dari kode dan menggunakannya tetapi tolong verifikasi itu.

Marquez
sumber
Mengapa implementasi itu menggunakan XOR (^ =) bukan 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
sumber
2
Anda dapat melewati ArrayList: byte statis publik [] longToBytes (long l) {long num = l; byte [] byte = byte baru [8]; untuk (int i = bytes.length - 1, i> = 0; i--) {bytes [i] = (byte) (num & 0xff); num >> = 8; } kembalikan bytesp; }
eckes
Metode asli tidak bekerja dengan angka negatif karena mendapat dalam loop tak terbatas sementara (l! = 0), metode @ eckes tidak bekerja dengan angka lebih dari 127 karena ia tidak memperhitungkan byte yang negatif dari 127 karena penyebab mereka sudah ditandatangani.
Big_Bad_E
2

Saya akan menambahkan jawaban lain yang merupakan jawaban tercepat ׂ (ya, bahkan lebih dari jawaban yang diterima), TETAPI itu tidak akan berfungsi untuk setiap kasus. NAMUN, itu AKAN bekerja untuk setiap skenario yang mungkin:

Anda cukup menggunakan String sebagai perantara. Catatan, ini AKAN MEMBERIKAN hasil yang benar meskipun sepertinya menggunakan String mungkin menghasilkan hasil yang salah SEBAGAI PANJANG ANDA TAHU ANDA BEKERJA DENGAN STRING "NORMAL". Ini adalah metode untuk meningkatkan efektivitas dan membuat kode lebih sederhana yang sebagai gantinya harus menggunakan beberapa asumsi pada string data yang dioperasikan.

Con menggunakan metode ini: Jika Anda bekerja dengan beberapa karakter ASCII seperti simbol-simbol ini di awal tabel ASCII, baris berikut mungkin gagal, tapi mari kita hadapi itu - Anda mungkin tidak akan pernah menggunakannya.

Pro menggunakan metode ini: Ingat bahwa kebanyakan orang biasanya bekerja dengan beberapa string normal tanpa karakter yang tidak biasa dan kemudian metode ini adalah cara termudah dan tercepat untuk dilakukan.

dari Panjang ke byte []:

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

dari byte [] ke Panjang:

long longVar = Long.valueOf(new String(byteArr)).longValue();
Yonatan Nir
sumber
2
Maaf untuk necroposting, tapi itu salah. E. g. String.valueOf (0) .getBytes () [0] == 0x30. Mengherankan! String # getBytes akan mengembalikan simbol digit yang disandikan ASCII, bukan digit: '0'! = 0, tetapi '0' == 0x30
Kirill Gamazkov
Yah mungkin jika Anda telah membaca seluruh jawaban saya maka Anda akan melihat saya telah memperingatkan tentang hal itu dalam jawaban itu sendiri
Yonatan Nir
1
Ah, saya melewatkan titik bahwa data [byte] perantara dianggap sebagai (hampir) buram. Trik Anda akan lakukan untuk skenario ini.
Kirill Gamazkov
1

Jika Anda sudah menggunakan a OutputStreamuntuk menulis ke soket, maka DataOutputStream mungkin cocok. Berikut ini sebuah contoh:

// Assumes you are currently working with a SocketOutputStream.

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

DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

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

Ada metode yang sama untuk short, int, float, dll Anda kemudian dapat menggunakan DataInputStream di sisi penerima.

Matt Solnit
sumber
0

Berikut cara lain untuk mengkonversi byte[]ke longmenggunakan Java 8 atau yang lebih baru:

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

Konversi a longdapat dinyatakan sebagai bit orde tinggi dan rendah dari dua nilai integer yang dikenakan bitwise-OR. Perhatikan bahwa toUnsignedLongini dari Integerkelas dan panggilan pertama ke toUnsignedLongmungkin berlebihan.

Konversi yang berlawanan dapat dibatalkan juga, seperti yang lain telah disebutkan.

Dave Jarvis
sumber
0

Ekstensi Kotlin untuk tipe Long dan 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()
}

Anda dapat melihat kode lengkap di perpustakaan saya https://github.com/ArtemBotnev/low-level-extensions

Artem Botnev
sumber