Échange d'octets d'un entier en Java
J'ai cette petite fonction qui attend un intet retourne un intavec les octets échangés:
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);
}
}
Fondamentalement, nous basculons entre endianess. Maintenant, y a-t-il un moyen meilleur / plus efficace d'accomplir la tâche?
Réponses
Note historique
Le tout premier commentaire sur la question du PO - bien avant que des réponses ne soient publiées - était une question posée par moi-même, et il posait très clairement:
Sans utiliser Integer.reverseBytes (cafeBabe) ?
La question était double:
- Faites remarquer à l'OP qu'il existe une méthode intégrée et qu'elle est probablement la manière la plus optimale de le faire.
- Demandez-leur s'ils essaient de réinventer la roue .
Ce commentaire / question a été supprimé. Si le PO y a répondu, la réponse a également été supprimée et je ne l'ai jamais vue.
Cette réponse ne préconise pas le PO réinventer la roue . La JVM / JIT Java aura implémenté la manière la plus efficace d'effectuer l'opération, en utilisant des fonctionnalités internes (telles que @HotSpotIntrinsicCandidate) qui ne sont pas nécessairement disponibles pour l'utilisateur final.
Réinventer la roue
public static int byteSwap(int a) {
return ((a & 0xff000000) >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
((a & 0x000000ff) << 24);
}
Cette fonction utilise 6 constantes distinctes, 4 opérations ET, 4 décalages et 3 opérations OR.
Il n'y a aucun intérêt avec l'opération AND in ((a & 0xff000000) >>> 24)ou in ((a & 0x000000ff) << 24)puisque les 24 bits qui sont masqués sont immédiatement décalés de toute façon. Cela supprime 2 constantes et 2 opérations AND, ce qui réduit le code à:
public static int byteSwap(int a) {
return (a >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
(a << 24);
}
En retravaillant l'ordre de la deuxième opération, nous pouvons supprimer une autre constante, la 0x00ff0000, et à la place réutiliser la 0xff00constante. Cela élimine le chargement de la troisième constante de masque de bits, ce qui permet d'économiser des instructions supplémentaires de code d'octet JVM:
public static int byteSwap(int a) {
return (a >>> 24) |
((a >>> 8) & 0xff00) |
((a & 0xff00) << 8) |
(a << 24);
}
Cette version retravaillée est très similaire au Integer.reverseBytes()code intégré dans la bibliothèque système:
@HotSpotIntrinsicCandidate
public static int reverseBytes(int i) {
return (i << 24) |
((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) |
(i >>> 24);
}
Je ne sais pas si la différence de commande gagne en vitesse supplémentaire, mais (en dollars en beignets) l' @HotSpotIntrinsicCandidateannotation le fait probablement - alors utilisez simplement la fonction intégrée.
Je l'aime, simple à lire.
Personnellement, je préfère garder les opérateurs sur la nouvelle ligne, cela permet de voir plus facilement comment les lignes s'articulent:
return ((a & 0xff000000) >>> 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8)
| ((a & 0x000000ff) << 24);
Pour suivre votre convention de mise en forme.
Vous pouvez mettre le premier et le dernier octet l'un sous l'autre, pour rendre le motif encore plus visible:
return ((a & 0xff000000) >>> 24)
| ((a & 0x000000ff) << 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8);
Cela supprime le motif des masques, mais permet de déduire plus facilement comment les octets sont déplacés.
Vous pouvez également convertir le inten un bytetableau (par exemple à l'aide de a ByteBuffer), et inverser ce tableau avant de le reconvertir. Mais en ce qui concerne l'efficacité, je pense que ce devrait être celui avec le moins d'opérations, car fondamentalement toute opération sur un bytetableau devrait faire la même chose et plus.
Comme déjà souligné, il existe déjà Integer.reverseBytes(int)ce qui est disponible depuis 1.5.