I have a byte array filled with hex numbers and printing it the easy way is pretty pointless because there are many unprintable elements. What I need is the exact hexcode in
Here are some common options ordered from simple (one-liner) to complex (huge library). If you are interested in performance, see the micro benchmarks below.
One very simple solution is to use the BigInteger
's hex representation:
new BigInteger(1, someByteArray).toString(16);
Note that since this handles numbers not arbitrary byte-strings it will omit leading zeros - this may or may not be what you want (e.g. 000AE3
vs 0AE3
for a 3 byte input). This is also very slow, about 100x slower compared to option 2.
Using the %X
placeholder, String.format() is able to encode most primitive types (short
, int
, long
) to hex:
String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());
If you exclusively have 4 bytes arrays you can use the toHexString method of the Integer class:
Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());
The same works with 8 byte arrays and Long
Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());
Here is a full featured, copy & pasteable code snippet supporting upper/lowercase and endianness. It is optimized to minimize memory complexity and maximize performance and should be compatible with all modern Java versions (5+).
private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
// our output size will be exactly 2x byte-array length
final char[] buffer = new char[byteArray.length * 2];
// choose lower or uppercase lookup table
final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
int index;
for (int i = 0; i < byteArray.length; i++) {
// for little endian we count from last to first
index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
// extract the upper 4 bit and look up char (0-A)
buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
// extract the lower 4 bit and look up char (0-A)
buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
}
return new String(buffer);
}
public static String encode(byte[] byteArray) {
return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}
The full source code with Apache v2 license and decoder can be found here.
While working on my previous project, I created this little toolkit for working with bytes in Java. It has no external dependencies and is compatible with Java 7+. It includes, among others, a very fast and well tested HEX en/decoder:
import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()
You can check it out on Github: bytes-java.
Of course there is the good 'ol commons codecs. (warning opinion ahead) While working on the project outlined above I analyzed the code and was quite disappointed; a lot of duplicate unorganized code, obsolete and exotic codecs probably only useful for very few and quite over engineered and slow implementations of popular codecs (specifically Base64). I therefore would make an informed decision if you want to use it or an alternative. Anyways, if you still want to use it, here is a code snippet:
import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));
More often than not you already have Guava as a dependency. If so just use:
import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);
If you use the Spring framework with Spring Security you can use the following:
import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));
If you already use the security framework Bouncy Castle you can use its Hex
util:
import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);
In previous Java (8 and below) versions the Java code for JAXB was included as runtime dependency. Since Java 9 and Jigsaw modularisation your code cannot access other code outside of it's module without explicit declaration. So be aware if you get an exception like:
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
when running on a JVM with Java 9+. If so then switch implementations to any of the alternatives above. See also this question.
Here are results from a simple JMH micro benchmark encoding byte arrays of different sizes. The values are operations per second, so higher is better. Note that micro benchmarks very often do not represent real world behavior, so take these results with a grain of salt.
| Name (ops/s) | 16 byte | 32 byte | 128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger | 2,088,514 | 1,008,357 | 133,665 | 4 |
| Opt2/3: Bytes Lib | 20,423,170 | 16,049,841 | 6,685,522 | 825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 | 529 |
| Opt5: Guava | 10,177,925 | 6,937,833 | 2,094,658 | 257 |
| Opt6: Spring | 18,704,986 | 13,643,374 | 4,904,805 | 601 |
| Opt7: BC | 7,501,666 | 3,674,422 | 1,077,236 | 152 |
| Opt8: JAX-B | 13,497,736 | 8,312,834 | 2,590,940 | 346 |
Specs: JDK 8u202, i7-7700K, Win10, 24GB Ram. See the full benchmark here.