If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our **Contribution Guidelines**.

# Converting Between Byte Arrays and Hexadecimal Strings in Java

Last modified: May 23, 2022

## 1. Overview

In this tutorial, we'll take a look at different ways to convert a byte array to a hexadecimal *String,* and vice versa.

We'll also understand the conversion mechanism and write our implementation to achieve this.

## 2. Converting Between Byte and Hexadecimal

First of all, let's take a look at the conversion logic between byte and hexadecimal numbers.

### 2.1. Byte to Hexadecimal

The bytes are 8 bit signed integers in Java. Therefore, we need to **convert each 4-bit segment to hex separately and concatenate them**. Consequently, we'll get two hexadecimal characters after conversion.

For instance, we can write 45 as 0010 1101 in binary, and the hexadecimal equivalent will be “2d”:

```
0010 = 2 (base 10) = 2 (base 16)
1101 = 13 (base 10) = d (base 16)
Therefore: 45 = 0010 1101 = 0x2d
```

Let's implement this simple logic in Java:

```
public String byteToHex(byte num) {
char[] hexDigits = new char[2];
hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
hexDigits[1] = Character.forDigit((num & 0xF), 16);
return new String(hexDigits);
}
```

Now, let's understand the above code by analyzing each operation. First of all, we created a char array of length 2 to store the output:

`char[] hexDigits = new char[2];`

Next, we isolated higher order bits by right shifting 4 bits. And then, we applied a mask to isolate lower order 4 bits. Masking is required because negative numbers are internally represented as two's complement of the positive number:

`hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);`

Then we convert the remaining 4 bits to hexadecimal:

`hexDigits[1] = Character.forDigit((num & 0xF), 16);`

Finally, we create a *String* object from the char array. And then, returned this object as converted hexadecimal array.

Now, let us understand how this will work for a negative byte -4:

```
hexDigits[0]:
1111 1100 >> 4 = 1111 1111 1111 1111 1111 1111 1111 1111
1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111 = 0xf
hexDigits[1]:
1111 1100 & 0xF = 0000 1100 = 0xc
Therefore: -4 (base 10) = 1111 1100 (base 2) = fc (base 16)
```

It's also worth noting that the *Character.*forDigit*()* method always returns lowercase characters.

### 2.2. Hexadecimal to Byte

Now, let's convert a hexadecimal digit to byte. As we know, a byte contains 8 bits. Therefore, **we need two hexadecimal digits to create one byte**.

First of all, we'll convert each hexadecimal digit into binary equivalent separately.

And then, we need to concatenate the two four bit-segments to get the byte equivalent:

```
Hexadecimal: 2d
2 = 0010 (base 2)
d = 1101 (base 2)
Therefore: 2d = 0010 1101 (base 2) = 45
```

Now, let's write the operation in Java:

```
public byte hexToByte(String hexString) {
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
return (byte) ((firstDigit << 4) + secondDigit);
}
private int toDigit(char hexChar) {
int digit = Character.digit(hexChar, 16);
if(digit == -1) {
throw new IllegalArgumentException(
"Invalid Hexadecimal Character: "+ hexChar);
}
return digit;
}
```

Let's understand this, one operation at a time.

First of all, we converted hexadecimal characters into integers:

```
int firstDigit = toDigit(hexString.charAt(0));
int secondDigit = toDigit(hexString.charAt(1));
```

Then we left shifted most significant digit by 4 bits. Consequently, the binary representation has zeros at four least significant bits.

Then, we added the least significant digit to it:

`return (byte) ((firstDigit << 4) + secondDigit);`

Now, let's examine the *toDigit()* method closely. We are using the *Character.digit()* method for conversion. **If the character value passed to this method is not a valid digit in the specified radix, -1 is returned.**

We're validating the return value and throwing an exception if an invalid value was passed.

## 3. Converting Between Byte Arrays and Hexadecimal *Strings*

At this point, we know how to convert a byte to the hexadecimal, and vice versa. Let's scale this algorithm and convert byte array to/from hexadecimal *String*.

### 3.1. Byte Array to Hexadecimal *String*

We need to loop through the array and generate hexadecimal pair for each byte:

```
public String encodeHexString(byte[] byteArray) {
StringBuffer hexStringBuffer = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
hexStringBuffer.append(byteToHex(byteArray[i]));
}
return hexStringBuffer.toString();
}
```

As we already know, the output will always be in lowercase.

### 3.2. Hexadecimal String to Byte Array

First of all, we need to check if the length of the hexadecimal *String* is an even number. This is because a hexadecimal *String* with odd length will result in incorrect byte representation.

Now, we'll iterate through the array and convert each hexadecimal pair to a byte:

```
public byte[] decodeHexString(String hexString) {
if (hexString.length() % 2 == 1) {
throw new IllegalArgumentException(
"Invalid hexadecimal String supplied.");
}
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
}
return bytes;
}
```

## 4. Using the *BigInteger* Class

We can **create an object of type BigInteger by passing a signum and byte array**.

Now, we can generate the hexadecimal *String* with the help of static method format defined in *String* class:

```
public String encodeUsingBigIntegerStringFormat(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
return String.format(
"%0" + (bytes.length << 1) + "x", bigInteger);
}
```

The format provided will generate a zero-padded lowercase hexadecimal *String.* We can also generate an uppercase string by replacing “x” with “X”.

Alternatively, we could've used the *toString()* method from *BigInteger*. The subtle **difference of using the toString() method is that the output isn't padded with leading zeros**:

```
public String encodeUsingBigIntegerToString(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
return bigInteger.toString(16);
}
```

Now, let's take a look at hexadecimal *String* to *byte* Array conversion:

```
public byte[] decodeUsingBigInteger(String hexString) {
byte[] byteArray = new BigInteger(hexString, 16)
.toByteArray();
if (byteArray[0] == 0) {
byte[] output = new byte[byteArray.length - 1];
System.arraycopy(
byteArray, 1, output,
0, output.length);
return output;
}
return byteArray;
}
```

**The toByteArray() method produces an additional sign bit**. We have written specific code for handling this additional bit.

Hence, we should be aware of these details before using the *BigInteger* class for the conversion.

## 5. Using the *DataTypeConverter* Class

The *DataTypeConverter* class is supplied with JAXB library. This is part of the standard library until Java 8. Starting from Java 9, we need to add *java.xml.bind* module to the runtime explicitly.

Let's take a look at implementation using the *DataTypeConverter* class:

```
public String encodeUsingDataTypeConverter(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
public byte[] decodeUsingDataTypeConverter(String hexString) {
return DatatypeConverter.parseHexBinary(hexString);
}
```

As displayed above, it is very convenient to use *DataTypeConverter* class. The **output of the printHexBinary() method is always in uppercase**. This class supplies a set of print and parse methods for data type conversion.

Before choosing this approach, we need to make sure the class will be available at runtime.

## 6. Using Apache's Commons-Codec Library

We can use the *Hex* class supplied with the Apache commons-codec library:

```
public String encodeUsingApacheCommons(byte[] bytes)
throws DecoderException {
return Hex.encodeHexString(bytes);
}
public byte[] decodeUsingApacheCommons(String hexString)
throws DecoderException {
return Hex.decodeHex(hexString);
}
```

The **output of encodeHexString is always in lowercase**.

## 7. Using Google's Guava Library

Let's take a look at how *BaseEncoding* class can be used for encoding and decoding byte array to the hexadecimal *String:*

```
public String encodeUsingGuava(byte[] bytes) {
return BaseEncoding.base16().encode(bytes);
}
public byte[] decodeUsingGuava(String hexString) {
return BaseEncoding.base16()
.decode(hexString.toUpperCase());
}
```

**The BaseEncoding encodes and decodes using uppercase characters by default**. If we need to use lowercase characters, a new encoding instance should be created using static method lowercase.

## 8. Conclusion

In this article, we learned the conversion algorithm between byte array to hexadecimal *String*. We also discussed various methods to encode byte array to hex string and vice versa.

It isn't advised to add a library to use a couple of utility methods only. Therefore, if we aren't using the external libraries already, we should use the algorithm discussed. The *DataTypeConverter* class is another way to encode/decode between various data types.

Finally, the complete source code of this tutorial is available on GitHub.