Trao đổi byte của một số nguyên trong Java

Sep 14 2020

Tôi có hàm nhỏ này mong đợi một intvà trả về một intvới các byte được hoán đổi:

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

Về cơ bản, chúng tôi đang chuyển đổi giữa độ bền. Bây giờ, có cách nào tốt hơn / hiệu quả hơn để hoàn thành nhiệm vụ không?

Trả lời

15 AJNeufeld Sep 14 2020 at 10:47

Ghi chú lịch sử

Nhận xét đầu tiên về câu hỏi của OP - rất lâu trước khi bất kỳ câu trả lời nào được đăng - là một câu hỏi do chính tôi đặt ra, và nó hỏi rất rõ ràng:

Nếu không sử dụng Integer.reverseBytes (cafeBabe) ?

Câu hỏi có hai phần:

  1. Chỉ ra với OP rằng có một phương thức tích hợp sẵn và có thể là cách tối ưu nhất để thực hiện điều này.
  2. Hỏi xem họ có đang cố gắng phát minh lại bánh xe không .

Bình luận / câu hỏi đó đã bị xóa. Nếu OP phản hồi nó, phản hồi cũng đã bị xóa và tôi không bao giờ thấy nó.

Câu trả lời này không ủng hộ OP phát minh lại bánh xe . Java JVM / JIT sẽ triển khai cách hiệu quả nhất để thực hiện hoạt động, sử dụng các tính năng nội bộ (chẳng hạn như @HotSpotIntrinsicCandidate) không nhất thiết phải có sẵn cho người dùng cuối.

Phát minh lại bánh xe

public static int byteSwap(int a) {
    return ((a & 0xff000000) >>> 24) |
           ((a & 0x00ff0000) >>> 8)  |
           ((a & 0x0000ff00) << 8)   |
           ((a & 0x000000ff) << 24);
}

Hàm này sử dụng 6 hằng số riêng biệt, 4 phép toán AND, 4 phép dịch chuyển và 3 phép toán OR.

Không có ích lợi gì với hoạt động AND trong ((a & 0xff000000) >>> 24)hoặc ngoài ((a & 0x000000ff) << 24)vì 24 bit đang được che dấu sẽ ngay lập tức được chuyển ra ngoài. Điều này loại bỏ 2 hằng số và 2 hoạt động AND, làm giảm mã thành:

public static int byteSwap(int a) {
    return (a >>> 24)               |
           ((a & 0x00ff0000) >>> 8) |
           ((a & 0x0000ff00) << 8)  |
           (a << 24);
}

Bằng cách làm lại thứ tự của hoạt động thứ hai, chúng ta có thể loại bỏ một hằng số khác 0x00ff0000, và thay vào đó sử dụng lại 0xff00hằng số. Điều này giúp loại bỏ việc tải hằng số mặt nạ bit thứ ba, lưu các lệnh mã byte JVM bổ sung:

public static int byteSwap(int a) {
    return (a >>> 24)           |
           ((a >>> 8) & 0xff00) |
           ((a & 0xff00) << 8)  |
           (a << 24);
}

Phiên bản được làm lại này rất giống với Integer.reverseBytes()mã tích hợp trong thư viện hệ thống:

@HotSpotIntrinsicCandidate
public static int reverseBytes(int i) {
    return (i << 24)            |
           ((i & 0xff00) << 8)  |
           ((i >>> 8) & 0xff00) |
           (i >>> 24);
}

Tôi không biết liệu sự khác biệt trong việc sắp xếp thứ tự có tăng thêm tốc độ hay không, nhưng @HotSpotIntrinsicCandidatechú thích có thể có - vì vậy chỉ cần sử dụng chức năng tích hợp sẵn.

6 Bobby Sep 14 2020 at 03:25

Tôi thích nó, đơn giản để đọc.


Cá nhân tôi muốn giữ các toán tử trên dòng mới, giúp dễ dàng xem các dòng thuộc về nhau như thế nào:

return ((a & 0xff000000) >>> 24)
     | ((a & 0x00ff0000) >>> 8)
     | ((a & 0x0000ff00) << 8)
     | ((a & 0x000000ff) << 24);

Để tuân theo quy ước định dạng của bạn.


Bạn có thể đặt byte đầu tiên và byte cuối cùng bên dưới nhau, để làm cho mẫu hiển thị rõ hơn:

return ((a & 0xff000000) >>> 24)
     | ((a & 0x000000ff) << 24)
     | ((a & 0x00ff0000) >>> 8)
     | ((a & 0x0000ff00) << 8);

Điều đó loại bỏ mẫu khỏi mặt nạ, nhưng cho phép dễ dàng suy luận cách các byte đang được di chuyển.


Bạn cũng có thể chuyển đổi intthành một bytemảng (ví dụ: với sự trợ giúp của a ByteBuffer) và đảo ngược mảng đó trước khi chuyển đổi lại. Nhưng về mặt hiệu quả, tôi nghĩ đây phải là thao tác có ít thao tác nhất, vì về cơ bản bất kỳ thao tác nào trên một bytemảng sẽ phải thực hiện điều tương tự và hơn thế nữa.


Như đã chỉ ra, đã Integer.reverseBytes(int)có sẵn từ 1.5.