Menukar byte dari integer di Java
Saya memiliki fungsi kecil ini yang mengharapkan intdan mengembalikan intdengan byte yang ditukar:
public class Main {
public static void main(String[] args) {
int cafeBabe = 0xcafebabe;
System.out.println(Integer.toHexString(cafeBabe));
System.out.println(Integer.toHexString(byteSwap(cafeBabe)));
System.out.println(Integer.toHexString(byteSwap(byteSwap(cafeBabe))));
}
public static int byteSwap(int a) {
return ((a & 0xff000000) >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
((a & 0x000000ff) << 24);
}
}
Pada dasarnya, kami beralih di antara endianess. Sekarang, adakah cara yang lebih baik / lebih efisien untuk menyelesaikan tugas?
Jawaban
Catatan Sejarah
Komentar pertama atas pertanyaan OP - jauh sebelum jawaban diposting - adalah pertanyaan yang saya tanyakan sendiri, dan dengan sangat tajam ditanyakan:
Tanpa menggunakan Integer.reverseBytes (cafeBabe) ?
Pertanyaannya dua kali lipat:
- Tunjukkan kepada OP bahwa ada metode bawaan, dan kemungkinan merupakan cara paling optimal untuk melakukan ini.
- Tanyakan apakah mereka mencoba menemukan kembali kemudi .
Komentar / pertanyaan itu telah dihapus. Jika OP menanggapinya, tanggapan juga telah dihapus dan saya tidak pernah melihatnya.
Jawaban ini tidak mendukung OP menemukan kembali roda . Java JVM / JIT akan menerapkan cara yang paling efisien untuk melakukan operasi, menggunakan fitur internal (seperti @HotSpotIntrinsicCandidate) yang belum tentu tersedia untuk pengguna akhir.
Menemukan kembali roda
public static int byteSwap(int a) {
return ((a & 0xff000000) >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
((a & 0x000000ff) << 24);
}
Fungsi ini menggunakan 6 konstanta berbeda, 4 operasi AND, 4 shift, dan 3 operasi OR.
Tidak ada gunanya operasi AND di dalam ((a & 0xff000000) >>> 24)atau di dalam ((a & 0x000000ff) << 24)karena 24 bit yang disamarkan akan segera dialihkan. Ini menghapus 2 konstanta, dan 2 operasi AND, yang mengurangi kode menjadi:
public static int byteSwap(int a) {
return (a >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
(a << 24);
}
Dengan mengerjakan ulang urutan operasi kedua, kita dapat menghapus konstanta lain, the 0x00ff0000, dan sebagai gantinya menggunakan kembali 0xff00konstanta tersebut. Ini menghilangkan pemuatan konstanta bit mask ketiga, menghemat instruksi kode byte JVM tambahan:
public static int byteSwap(int a) {
return (a >>> 24) |
((a >>> 8) & 0xff00) |
((a & 0xff00) << 8) |
(a << 24);
}
Versi yang dikerjakan ulang ini sangat mirip dengan Integer.reverseBytes()kode bawaan di pustaka sistem:
@HotSpotIntrinsicCandidate
public static int reverseBytes(int i) {
return (i << 24) |
((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) |
(i >>> 24);
}
Saya tidak tahu apakah perbedaan dalam memesan mendapatkan kecepatan tambahan, tetapi (dolar untuk donat) @HotSpotIntrinsicCandidateanotasi mungkin melakukannya - jadi gunakan saja fungsi bawaan.
Saya suka, mudah dibaca.
Secara pribadi, saya lebih suka membiarkan operator di jalur baru, membuatnya lebih mudah untuk melihat bagaimana garis-garis itu saling terkait:
return ((a & 0xff000000) >>> 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8)
| ((a & 0x000000ff) << 24);
Untuk mengikuti konvensi pemformatan Anda.
Anda dapat meletakkan byte pertama dan terakhir di bawah satu sama lain, untuk membuat pola terlihat lebih baik:
return ((a & 0xff000000) >>> 24)
| ((a & 0x000000ff) << 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8);
Itu menghilangkan pola dari topeng, tetapi memungkinkan untuk lebih mudah menyimpulkan bagaimana byte dipindahkan.
Anda juga bisa mengonversi intmenjadi bytearray (misalnya dengan bantuan a ByteBuffer), dan membalikkan array itu sebelum mengubahnya kembali. Tetapi sejauh efektivitas berjalan, saya pikir ini harus menjadi salah satu dengan operasi paling sedikit, karena pada dasarnya setiap operasi pada bytearray harus melakukan hal yang sama dan banyak lagi.
Seperti yang sudah ditunjukkan, sudah ada Integer.reverseBytes(int)yang tersedia sejak 1.5.