Ubah array byte menjadi integer di Java dan sebaliknya

147

Saya ingin menyimpan beberapa data ke dalam array byte di Jawa. Pada dasarnya hanya angka yang bisa memakan waktu hingga 2 Bytes per angka.

Saya ingin tahu bagaimana saya dapat mengubah integer menjadi array byte sepanjang 2 byte dan sebaliknya. Saya menemukan banyak solusi googling tetapi kebanyakan dari mereka tidak menjelaskan apa yang terjadi dalam kode. Ada banyak hal yang berubah yang saya tidak begitu mengerti jadi saya akan menghargai penjelasan dasarnya.

Chris
sumber
4
Berapa banyak yang Anda mengerti tentang sedikit pergeseran? Kedengarannya seperti pertanyaan sebenarnya adalah "apa yang dilakukan bit shifting" lebih dari tentang konversi ke array byte, sungguh - jika Anda benar-benar ingin memahami bagaimana konversi akan bekerja.
Jon Skeet
1
(Hanya untuk memperjelas, saya baik-baik saja dengan kedua pertanyaan tersebut, tetapi ada baiknya memperjelas pertanyaan mana yang benar-benar ingin Anda jawab. Kemungkinan besar Anda akan mendapatkan jawaban yang lebih berguna bagi Anda.)
Jon Skeet
Oke, saya mengerti maksud Anda! Terima kasih atas komentarnya. Saya tahu bit shifting apa yang saya tidak mengerti apa yang digunakan untuk mengubah array byte.
Chris
3
@prekageo dan Jeff Mercado Terima kasih atas dua jawaban Anda. prekageo memberikan penjelasan yang bagus tentang bagaimana ini dilakukan, tautan yang bagus! Itu membuatnya jauh lebih jelas bagi saya. Dan solusi Jeff Mercados memecahkan masalah yang saya miliki.
Chris

Jawaban:

237

Gunakan kelas yang ditemukan di java.nionamespace, khususnya ByteBuffer,. Itu bisa melakukan semua pekerjaan untuk Anda.

byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1

ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }
Jeff Mercado
sumber
2
Apakah terlalu mahal jika array byte hanya berisi 1 atau 2 integer? Tidak yakin dengan biaya pembangunannya a ByteBuffer.
Kucing Meong 2012
Seberapa sering Anda bekerja dengan data biner dalam potongan 2-4 byte? Betulkah? Implementasi yang waras akan bekerja dengannya di BUFSIZ chunks (biasanya 4kb) atau menggunakan library IO lain yang menyembunyikan detail ini. Ada seluruh pustaka dalam kerangka kerja yang didedikasikan untuk membantu Anda mengerjakan buffer data. Anda merugikan diri sendiri dan pengelola kode lainnya saat Anda menerapkan operasi umum tanpa alasan yang kuat (baik itu kinerja atau operasi penting lainnya). Buffer ini hanyalah pembungkus yang beroperasi pada array, tidak lebih.
Jeff Mercado
Kenapa Anda bisa membuat contoh kelas abstrak?
1
@JaveneCPPMcGowan Tidak ada contoh langsung dalam jawaban ini. Jika yang Anda maksud adalah metode pabrik wrapdan allocate, metode tersebut tidak mengembalikan instance dari kelas abstrak ByteBuffer.
Marko Topolnik
Bukan solusi untuk langkah 3 byte. Kita bisa mendapatkan Char, Short, Int. Saya kira saya bisa memasukkan hingga 4 byte dan membuang 4 byte setiap kali, tapi saya lebih suka tidak.
Yohanes
132
byte[] toByteArray(int value) {
     return  ByteBuffer.allocate(4).putInt(value).array();
}

byte[] toByteArray(int value) {
    return new byte[] { 
        (byte)(value >> 24),
        (byte)(value >> 16),
        (byte)(value >> 8),
        (byte)value };
}

int fromByteArray(byte[] bytes) {
     return ByteBuffer.wrap(bytes).getInt();
}
// packing an array of 4 bytes to an int, big endian, minimal parentheses
// operator precedence: <<, &, | 
// when operators of equal precedence (here bitwise OR) appear in the same expression, they are evaluated from left to right
int fromByteArray(byte[] bytes) {
     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}

// packing an array of 4 bytes to an int, big endian, clean code
int fromByteArray(byte[] bytes) {
     return ((bytes[0] & 0xFF) << 24) | 
            ((bytes[1] & 0xFF) << 16) | 
            ((bytes[2] & 0xFF) << 8 ) | 
            ((bytes[3] & 0xFF) << 0 );
}

Saat mengemas byte yang ditandatangani ke dalam int, setiap byte perlu ditutup-tutupi karena itu diperpanjang tanda menjadi 32 bit (bukan diperpanjang nol) karena aturan promosi aritmatika (dijelaskan dalam JLS, Konversi, dan Promosi).

Ada teka-teki menarik terkait hal ini yang dijelaskan dalam Java Puzzlers ("A Big Delight in Every Byte") oleh Joshua Bloch dan Neal Gafter. Ketika membandingkan nilai byte dengan nilai int, byte tanda diperpanjang ke int dan kemudian nilai ini dibandingkan dengan int lainnya.

byte[] bytes = (…)
if (bytes[0] == 0xFF) {
   // dead code, bytes[0] is in the range [-128,127] and thus never equal to 255
}

Perhatikan bahwa semua tipe numerik ditandatangani di Java dengan pengecualian char sebagai tipe integer 16-bit unsigned.

Jarek Przygódzki
sumber
Saya pikir & 0xFFitu tidak perlu.
Ori Popowski
11
@LeifEricson Saya percaya & 0xFFs diperlukan karena memberitahu JVM untuk mengubah byte yang ditandatangani menjadi integer dengan hanya bit-bit yang ditetapkan. Jika tidak, byte -1 (0xFF) akan berubah menjadi int -1 (0xFFFFFFFF). Saya bisa saja salah dan bahkan jika saya salah, itu tidak menyakitkan dan membuat segalanya lebih jelas.
coderforlife
4
& 0xFF memang wajib. byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b & 0xFF, 16)); //Output: 88
HBN
1
@ptntialunrlsd Sebenarnya tidak. Sebelum Anda melakukan & mengoperasikan bytedengan 0xFF ( int), JVM akan mentransmisikan byteke intdengan 1 diperpanjang atau 0 diperpanjang sesuai dengan bit utama terlebih dahulu. Tidak ada byte unsigned di Java, bytes selalu ditandatangani.
Nier
2
Ketika mengurai int dari byte array, memperhatikan dengan ukuran array byte, jika itu lebih besar dari 4 byte, menurut doc dari ByteBuffer.getInt(): Reads the next four bytes at this buffer's current position, hanya 4 byte pertama akan diurai, yang seharusnya tidak apa yang Anda inginkan.
Bin
60

Anda juga dapat menggunakan BigInteger untuk byte dengan panjang variabel. Anda dapat mengubahnya menjadi panjang, int atau pendek, mana saja yang sesuai dengan kebutuhan Anda.

new BigInteger(bytes).intValue();

atau untuk menunjukkan polaritas:

new BigInteger(1, bytes).intValue();

Untuk mendapatkan kembali byte:

new BigInteger(bytes).toByteArray()
Jamel Toms
sumber
1
Perhatikan bahwa sejak 1,8, ini intValueExact, bukanintValue
Abhijit Sarkar
5

Implementasi dasar akan menjadi seperti ini:

public class Test {
    public static void main(String[] args) {
        int[] input = new int[] { 0x1234, 0x5678, 0x9abc };
        byte[] output = new byte[input.length * 2];

        for (int i = 0, j = 0; i < input.length; i++, j+=2) {
            output[j] = (byte)(input[i] & 0xff);
            output[j+1] = (byte)((input[i] >> 8) & 0xff);
        }

        for (int i = 0; i < output.length; i++)
            System.out.format("%02x\n",output[i]);
    }
}

Untuk memahami banyak hal, Anda dapat membaca artikel WP ini: http://en.wikipedia.org/wiki/Endianness

Kode sumber di atas akan dikeluarkan 34 12 78 56 bc 9a. 2 byte pertama ( 34 12) mewakili integer pertama, dll. Kode sumber di atas mengkodekan integer dalam format little endian.

prekageo
sumber
2
/** length should be less than 4 (for int) **/
public long byteToInt(byte[] bytes, int length) {
        int val = 0;
        if(length>4) throw new RuntimeException("Too big to fit in int");
        for (int i = 0; i < length; i++) {
            val=val<<8;
            val=val|(bytes[i] & 0xFF);
        }
        return val;
    }
Dhaval Rami
sumber
0

Seseorang dengan persyaratan di mana mereka harus membaca dari bit, katakanlah Anda harus membaca hanya dari 3 bit tetapi Anda memerlukan integer yang ditandatangani kemudian gunakan yang berikut:

data is of type: java.util.BitSet

new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3

Angka ajaib 3dapat diganti dengan jumlah bit (bukan byte) yang Anda gunakan.

Vishrant
sumber
0

Seringkali, jambu biji memiliki apa yang Anda butuhkan.

Untuk beralih dari array byte ke int Ints.fromBytesArray:, doc di sini

Untuk beralih dari int ke byte array Ints.toByteArray:, doc di sini

jeremie
sumber
-7

saya pikir ini adalah mode terbaik untuk dilemparkan ke int

   public int ByteToint(Byte B){
        String comb;
        int out=0;
        comb=B+"";
        salida= Integer.parseInt(comb);
        out=out+128;
        return out;
    }

byte comvert pertama ke String

comb=B+"";

langkah selanjutnya adalah comvert ke int

out= Integer.parseInt(comb);

tetapi byte berada dalam kemarahan -128 hingga 127 untuk alasan ini, saya pikir lebih baik menggunakan rage 0 hingga 255 dan Anda hanya perlu melakukan ini:

out=out+256;
Angel Salvador Ayala Ochoa
sumber
Ini salah. Pertimbangkan byte 0x01. Metode Anda akan menghasilkan 129 yang salah. 0x01 harus mengeluarkan bilangan bulat 1. Anda hanya harus menambahkan 128 jika bilangan bulat yang Anda dapatkan dari parseInt negatif.
disklosr
Maksud saya, Anda harus menambahkan 256, bukan 128. Tidak dapat mengeditnya setelah itu.
disklosr
ubah posting untuk menambahkan 256 karena mungkin berguna bagi orang lain!
apmartin1991
Ini melakukan banyak casting dan membuat objek baru (pikirkan melakukan ini untuk loop) yang dapat menurunkan kinerja, silakan periksa metode Integer.toString () untuk petunjuk tentang cara mengurai angka.
Marcos Vasconcelos
juga, saat memposting kode di stackoverflow, intinya adalah kode pos yang mudah dipahami. Kode yang mudah masuk akal harus memiliki pengenal yang dapat dimengerti . Dan di stackoverflow, berarti dimengerti dalam bahasa Inggris .
Mike Nakis