Tauschen von Bytes einer Ganzzahl in Java

Sep 14 2020

Ich habe diese winzige Funktion, die eine erwartet intund eine intmit den getauschten Bytes zurückgibt :

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

Grundsätzlich wechseln wir zwischen Endianess. Gibt es nun eine bessere / effizientere Möglichkeit, die Aufgabe zu erfüllen?

Antworten

15 AJNeufeld Sep 14 2020 at 10:47

Historischer Hinweis

Der allererste Kommentar zur Frage des OP - lange bevor irgendwelche Antworten veröffentlicht wurden - war eine Frage, die ich gestellt habe und die sehr deutlich gestellt wurde:

Ohne Integer.reverseBytes (cafeBabe) zu verwenden ?

Die Frage war zweifach:

  1. Weisen Sie das OP darauf hin, dass eine integrierte Methode vorhanden ist und dies wahrscheinlich die optimalste Methode ist.
  2. Fragen Sie, ob sie versuchen, das Rad neu zu erfinden .

Dieser Kommentar / diese Frage wurde entfernt. Wenn das OP darauf geantwortet hat, wurde die Antwort ebenfalls gelöscht und ich habe sie nie gesehen.

Diese Antwort spricht nicht dafür, dass das OP das Rad neu erfindet . Die Java JVM / JIT hat die effizienteste Methode zur Durchführung des Vorgangs implementiert, wobei interne Funktionen (z. B. @HotSpotIntrinsicCandidate) verwendet wurden, die dem Endbenutzer nicht unbedingt zur Verfügung stehen.

Das Rad neu erfinden

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

Diese Funktion verwendet 6 verschiedene Konstanten, 4 UND-Operationen, 4 Verschiebungen und 3 ODER-Operationen.

Es macht keinen Sinn, die UND-Operation ein- ((a & 0xff000000) >>> 24)oder ((a & 0x000000ff) << 24)auszuschalten, da die zu maskierenden 24 Bits ohnehin sofort herausgeschoben werden. Dadurch werden 2 Konstanten und 2 UND-Operationen entfernt, wodurch der Code auf Folgendes reduziert wird:

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

Durch Überarbeiten der Reihenfolge der zweiten Operation können wir eine andere Konstante entfernen, die 0x00ff0000, und stattdessen die 0xff00Konstante wiederverwenden . Dadurch entfällt das Laden der dritten Bitmaskenkonstante, wodurch zusätzliche Anweisungen für den JVM-Bytecode gespeichert werden:

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

Diese überarbeitete Version ist dem Integer.reverseBytes()in der Systembibliothek integrierten Code sehr ähnlich :

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

Ich weiß nicht, ob der Unterschied in der Bestellung zusätzliche Geschwindigkeit @HotSpotIntrinsicCandidatebringt , aber (Dollar zu Donuts) die Annotation wahrscheinlich - also verwenden Sie einfach die eingebaute Funktion.

6 Bobby Sep 14 2020 at 03:25

Ich mag es, einfach zu lesen.


Persönlich ziehe ich es vor, die Operatoren auf der neuen Linie zu halten, um leichter zu erkennen, wie die Linien zusammengehören:

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

So befolgen Sie Ihre Formatierungskonvention.


Sie können das erste und das letzte Byte untereinander setzen, um das Muster noch besser sichtbar zu machen:

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

Dadurch wird das Muster aus den Masken entfernt, es lässt sich jedoch leichter ableiten, wie die Bytes verschoben werden.


Sie können das Array auch intin ein byteArray konvertieren (z. B. mithilfe von a ByteBuffer) und dieses Array umkehren, bevor Sie es zurückkonvertieren. Was die Effektivität betrifft, sollte dies meiner Meinung nach diejenige mit den geringsten Operationen sein, da im Grunde jede Operation auf einem byteArray dasselbe und mehr tun müsste.


Wie bereits erwähnt, gibt es bereits Integer.reverseBytes(int)welche seit 1.5.