जावा में एक पूर्णांक की बाइट्स स्वैपिंग

Sep 14 2020

मेरे पास यह छोटा कार्य है जो एक उम्मीद करता है intऔर intबाइट्स स्वैप के साथ लौटता है :

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

असल में, हम एंडियन के बीच स्विच कर रहे हैं। अब, क्या कार्य को पूरा करने का एक बेहतर / अधिक कुशल तरीका है?

जवाब

15 AJNeufeld Sep 14 2020 at 10:47

ऐतिहासिक नोट

ओपी के सवाल पर पहली टिप्पणी - किसी भी उत्तर को पोस्ट करने से बहुत पहले - अपने आप से एक सवाल पूछा गया था, और यह बहुत स्पष्ट रूप से पूछा गया था:

Integer.reverseBytes (cafeBabe) का उपयोग किए बिना ?

सवाल दो गुना था:

  1. ओपी को इंगित करें कि एक अंतर्निहित पद्धति मौजूद है, और ऐसा करने का सबसे इष्टतम तरीका होने की संभावना है।
  2. पूछें कि क्या वे पहिया को फिर से बनाने की कोशिश कर रहे हैं ।

उस टिप्पणी / प्रश्न को हटा दिया गया है। यदि ओपी ने इसका जवाब दिया, तो प्रतिक्रिया को हटा दिया गया है और मैंने इसे कभी नहीं देखा।

यह जवाब ओपी रिइनवेंट-द-व्हील की वकालत नहीं कर रहा है । जावा जेवीएम / जेआईटी ने आंतरिक सुविधाओं (जैसे @HotSpotIntrinsicCandidate) का उपयोग करते हुए ऑपरेशन को निष्पादित करने का सबसे कुशल तरीका लागू किया होगा, जो आवश्यक रूप से अंतिम-उपयोगकर्ता के लिए उपलब्ध नहीं हैं।

पहिया बदलते

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

यह फ़ंक्शन 6 अलग-अलग स्थिरांक, 4 और संचालन, 4 पारियों और 3 या संचालन का उपयोग करता है।

AND ऑपरेशन के साथ ((a & 0xff000000) >>> 24)या ((a & 0x000000ff) << 24)उसके बाद कोई बात नहीं है क्योंकि जिन 24 बिट्स को मास्क किया जा रहा है उन्हें तुरंत किसी भी तरह से स्थानांतरित कर दिया गया है। यह 2 स्थिरांक, और 2 और संचालन को हटाता है, जो कोड को कम करता है:

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

दूसरे ऑपरेशन के क्रम को फिर से काम करके, हम एक और स्थिरांक को हटा सकते हैं 0x00ff0000, और इसके बजाय 0xff00निरंतर का पुन: उपयोग कर सकते हैं । यह तीसरे बिट मास्क की लोडिंग को समाप्त करता है, अतिरिक्त JVM बाइट कोड निर्देशों को बचाता है:

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

यह पुनर्प्राप्त संस्करण Integer.reverseBytes()सिस्टम लाइब्रेरी में बिल्ट-इन कोड के समान है :

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

मुझे नहीं पता कि ऑर्डर करने में अंतर किसी अतिरिक्त गति को प्राप्त करता है, लेकिन (डोनट्स को डॉलर) @HotSpotIntrinsicCandidateएनोटेशन शायद करता है - इसलिए केवल अंतर्निहित फ़ंक्शन का उपयोग करें।

6 Bobby Sep 14 2020 at 03:25

मुझे यह पसंद है, पढ़ने में सरल।


निजी तौर पर, मैं नई लाइन पर ऑपरेटरों को रखना पसंद करता हूं, यह देखना आसान बनाता है कि लाइनें एक साथ कैसे होती हैं:

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

अपने प्रारूपण सम्मेलन का पालन करने के लिए।


आप पैटर्न को और भी बेहतर बनाने के लिए पहले और आखिरी बाइट को एक दूसरे के नीचे रख सकते हैं:

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

यह मास्क से पैटर्न को हटाता है, लेकिन बाइट्स को स्थानांतरित करने के तरीके को कम करने की अनुमति देता है।


आप intकिसी byteसरणी (उदाहरण के लिए ByteBuffer) की मदद से भी परिवर्तित कर सकते हैं , और उस सरणी को वापस बदलने से पहले उसे उल्टा कर सकते हैं। लेकिन जहां तक ​​प्रभावशीलता जाती है, मुझे लगता है कि यह सबसे कम संचालन वाला होना चाहिए, क्योंकि मूल रूप से किसी byteसरणी पर किसी भी ऑपरेशन को एक ही काम करना होगा और अधिक।


जैसा कि पहले ही बताया जा चुका है, वहाँ पहले से ही Integer.reverseBytes(int)1.5 से उपलब्ध है।