Home » Android » android – Hex To String in Java Performance is too slow

android – Hex To String in Java Performance is too slow

Posted by: admin June 15, 2020 Leave a comment

Questions:

I have the following method for my android program, written in Java.

The method takes in a string of hex and returns a string of the same text written in ascii.

public static String hexToString(String hex)
{
    StringBuilder sb = new StringBuilder();

    for (int count = 0; count < hex.length() - 1; count += 2)
    {
        String output = hex.substring(count, (count + 2));    //grab the hex in pairs

        int decimal = Integer.parseInt(output, 16);    //convert hex to decimal

        sb.append((char)decimal);    //convert the decimal to character
    }

    return sb.toString();
}

The method works fine, however my program is very time critical and this method is potentially being called tens of thousands of times. When analysing the slow bits of my program, this method takes up far too much time because of:

Integer.parseInt(output, 16);

and

hex.substring(count, (count + 2));

In the order of slowest first.

Does anyone know of a faster method of achieving the same thing?

How to&Answers:

Don’t create a new String on each iteration. One way to improve performance would be using a char array and applying math operations per character.

public static String hexToString(String hex) {
    StringBuilder sb = new StringBuilder();
    char[] hexData = hex.toCharArray();
    for (int count = 0; count < hexData.length - 1; count += 2) {
        int firstDigit = Character.digit(hexData[count], 16);
        int lastDigit = Character.digit(hexData[count + 1], 16);
        int decimal = firstDigit * 16 + lastDigit;
        sb.append((char)decimal);
    }
    return sb.toString();
}

More info about this method:

Also, if you’re parsing the hex string in pairs, you can use a look up table as @L7ColWinters suggests:

private static final Map<String, Character> lookupHex = new HashMap<String, Character>();

static {
    for(int i = 0; i < 256; i++) {
        String key = Integer.toHexString(i);
        Character value = (char)(Integer.parseInt(key, 16));
        lookupHex.put(key, value);
    }
}

public static String hexToString(String hex) {
    StringBuilder sb = new StringBuilder();
    for (int count = 0; count < hex.length() - 1; count += 2) {
        String output = hex.substring(count, (count + 2));
        sb.append((char)lookupHex.get(output));
    }
    return sb.toString();
}

Answer:

What about this one…

public static String hexToString(final String str) {
 return new String(new BigInteger(str, 16).toByteArray());
}

Answer:

Another alternative would be to just do some simple arithmetics:

public static int hexCharToInt(char c)
{
    int result = 0;
    if(c >= 'A' && c <= 'F')
    {
        result += (c - 'A' + 10);
    }
    else if( c >= '0' && c <= '9')
    {
            result += (c - '0');
    }
        return result;
    }

public static String hexToString(String hex)
{
    StringBuilder sb = new StringBuilder();

    for (int count = 0; count < hex.length() - 1; count += 2)
    {
        char c1 = hex.charAt(count);
        char c2 = hex.charAt(count + 1);

        int decimal = hexCharToInt(c1) * 16 + hexCharToInt(c2);

        sb.append((char)decimal);    //convert the decimal to character
    }

    return sb.toString();
}

Try it out and see which solution that works best on your system!

Answer:

This code was taken from Hex class of Apache Commons Codec and simplified a bit. (Removed some range checks etc. which is not necessary for understanding here. In practice you want to use the original implementation.)

/**
 * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The
 * returned array will be half the length of the passed array, as it takes two characters to represent any given
 * byte. An exception is thrown if the passed char array has an odd number of elements.
 * 
 * @param data
 *            An array of characters containing hexadecimal digits
 * @return A byte array containing binary data decoded from the supplied char array.
 * @throws DecoderException
 *             Thrown if an odd number or illegal of characters is supplied
 */
public static byte[] decodeHex(char[] data) throws DecoderException {

    int len = data.length;

    byte[] out = new byte[len >> 1];

    // two characters form the hex value.
    for (int i = 0, j = 0; j < len; i++) {
        int f = Character.digit(data[j], 16) << 4;
        j++;
        f = f | Character.digit(data[j], 16);
        j++;
        out[i] = (byte) (f & 0xFF);
    }

    return out;
}

You can use the returned byte[] to construct a String object afterwards.

So when using Apache Commons Codec then your method looks like this:

public static String hexToString(String hex) throws UnsupportedEncodingException, DecoderException {
    return new String(Hex.decodeHex(hex.toCharArray()), "US-ASCII");
  }