I have this tiny function that expects an int
and returns an int
with the bytes swapped:
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);
}
}
Basically, we are switching between endianess. Now, is there a better/more efficient way of accomplishing the task?
2 Answers 2
Historical Note
The very first comment on the OP's question -- long before any answers were posted -- was a question asked by myself, and it very pointedly asked:
Without using Integer.reverseBytes(cafeBabe)?
The question was two-fold:
- Point out to the OP that a built-in method exists, and is likely the most optimal way of doing this.
- Ask if they are trying to reinvent-the-wheel.
That comment/question has been removed. If the OP responded to it, the response has been deleted as well and I never saw it.
This answer is not advocating the OP reinvent-the-wheel. The Java JVM / JIT will have implemented the most efficient way of performing the operation, using internal features (such as @HotSpotIntrinsicCandidate
) that are not necessarily available to the end-user.
Reinventing the wheel
public static int byteSwap(int a) {
return ((a & 0xff000000) >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
((a & 0x000000ff) << 24);
}
This function uses 6 distinct constants, 4 AND operations, 4 shifts, and 3 OR operations.
There is no point with the AND operation in ((a & 0xff000000) >>> 24)
or in ((a & 0x000000ff) << 24)
since the 24 bits which are being masked are immediately shifted out anyway. This removes 2 constants, and 2 AND operations, which reduces the code to:
public static int byteSwap(int a) {
return (a >>> 24) |
((a & 0x00ff0000) >>> 8) |
((a & 0x0000ff00) << 8) |
(a << 24);
}
By reworking the order of second operation, we can remove another constant, the 0x00ff0000
, and instead reuse the 0xff00
constant. This eliminates the loading of the third bit mask constant, saving additional JVM byte code instructions:
public static int byteSwap(int a) {
return (a >>> 24) |
((a >>> 8) & 0xff00) |
((a & 0xff00) << 8) |
(a << 24);
}
This reworked version is very similar to the built-in Integer.reverseBytes()
code in the system library:
@HotSpotIntrinsicCandidate
public static int reverseBytes(int i) {
return (i << 24) |
((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) |
(i >>> 24);
}
I don't know if the difference in ordering gains any additional speed, but (dollars to donuts) the @HotSpotIntrinsicCandidate
annotation probably does - so just use the built-in function.
-
3\$\begingroup\$ While the masking in
(a & 0x000000FF) << 24
has no effect in this case, it sure adds its fair portion to readability. Since OP was asking for a more efficient way I'd at least mention that this kind of optimization is in the most obvious realms of micro optimizations, Also there is a builtin functionreverseBytes(int)
. \$\endgroup\$Num Lock– Num Lock2020年09月14日 10:47:26 +00:00Commented Sep 14, 2020 at 10:47
I like it, simple to read.
Personally, I prefer to keep operators on the new line, makes it easier to see how the lines belong together:
return ((a & 0xff000000) >>> 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8)
| ((a & 0x000000ff) << 24);
To follow your formatting convention.
You could putt the first and last byte underneath each other, to make the pattern even better visible:
return ((a & 0xff000000) >>> 24)
| ((a & 0x000000ff) << 24)
| ((a & 0x00ff0000) >>> 8)
| ((a & 0x0000ff00) << 8);
That removes the pattern from the masks, but allows to easier deduce how the bytes are being moved.
You could also convert the int
to a byte
array (for example with the help of a ByteBuffer
), and reverse that array before converting it back. But as far as effectiveness goes, I think this should be the one with the least operations, as basically any operation on a byte
array would have to do the same thing and more.
As already pointed out, there is already Integer.reverseBytes(int)
which is available since 1.5.
-
\$\begingroup\$ With
ByteBuffer
you can simply reverse the endianess and then read back theint
, no need to do that manually (always look at the methods of a class :) ). \$\endgroup\$Maarten Bodewes– Maarten Bodewes2020年09月14日 14:47:04 +00:00Commented Sep 14, 2020 at 14:47