我有一个字节数组。 我希望将该数组的每个字节String转换为相应的十六进制值。

Java中是否有将字节数组转换为十六进制的函数?


当前回答

如果你想要一个恒定宽度的十六进制表示,即0A而不是a,这样你就可以明确地恢复字节,尝试format():

StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
    result.append(String.format("%02X", bb));
}
return result.toString();

其他回答

我所发现的最快的方法是:

private static final String    HEXES    = "0123456789ABCDEF";

static String getHex(byte[] raw) {
    final StringBuilder hex = new StringBuilder(2 * raw.length);
    for (final byte b : raw) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}

它比String.format快50倍。如果你想测试它:

public class MyTest{
    private static final String    HEXES        = "0123456789ABCDEF";

    @Test
    public void test_get_hex() {
        byte[] raw = {
            (byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
            (byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
            (byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
            (byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
            (byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
            (byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
            (byte) 0xd6, (byte) 0x10,
        };

        int N = 77777;
        long t;

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                final StringBuilder hex = new StringBuilder(2 * raw.length);
                for (final byte b : raw) {
                    hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 50
        }

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                StringBuilder hex = new StringBuilder(2 * raw.length);
                for (byte b : raw) {
                    hex.append(String.format("%02X", b));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 2535
        }

    }
}

编辑:刚刚发现一些东西只是稍微快一点,保持在一行,但与JRE 9不兼容。使用风险自负

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);

如果您乐于使用外部库,则org.apache.commons.codec.binary.Hex类有一个encodeHex方法,该方法接受一个字节[]并返回一个char[]。这个方法比format选项快得多,并且封装了转换的细节。还附带了一个decodeHex方法,用于相反的转换。

这是一条非常快的路。不需要外部库。

final protected static char[] HEXARRAY = "0123456789abcdef".toCharArray();

    public static String encodeHexString( byte[] bytes ) {

        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEXARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEXARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }

只是添加我的两分,因为我看到许多答案使用字符数组和/或使用StringBuilder实例,并声称是快速或更快。

由于我使用ASCII表组织有一个不同的想法,代码点48-57为0-9,代码点65-70为a-f,代码点97-102为a-f,我想测试哪个想法是最快的。

因为我已经好几年没有做过类似的事情了,所以我要做一个广泛的拍摄。我在不同大小的数组中使用了10亿字节(1M, 1K, 10),因此每个数组有1000倍1M字节,每个数组有1M倍1000字节,每个数组有100M倍10字节。

结果是1-F中的char数组胜出。使用char数组而不是StringBuilder作为输出也很容易(对象更少,不需要测试容量,在增长时不需要新数组或复制)。此外,当使用foreach (for(var b: bytes))循环时,你似乎会得到一个小的惩罚。

使用我的想法的版本大约是。每个数组1M字节时慢15%,每个数组1000字节时慢21%,每个数组10字节时慢18%。StringBuilder版本分别慢了210%、380%和310%。

这很糟糕,但也不是那么出乎意料,因为在第一级缓存中查找小数组胜过if和add... .(一个缓存访问+偏移量计算vs.一个if,一个跳转,一个add ->不确定跳转thou)。

我的版本:

public static String bytesToHex(byte [] bytes) {
    
    char [] result = new char [bytes.length * 2];
    
    for(int index = 0; index < bytes.length; index++) {
        int v = bytes[index]; 

        int upper = (v >>> 4) & 0xF;
        result[index * 2] = (char)(upper + (upper < 10 ? 48 : 65 - 10));
        
        int lower = v & 0xF;
        result[index * 2 + 1] = (char)(lower + (lower < 10 ? 48 : 65 - 10));
    }
    
    return new String(result);
}

PS:是的,我做了多次测试,每次都做了最好的测试,做了热身,也做了100亿字符的测试,以确保相同的图片... .

这是我迄今为止发现的运行最快的代码。我在23ms内运行了109015个长度为32的字节数组。我在VM上运行它,所以它在裸机上可能会运行得更快。

public static final char[] HEX_DIGITS =         {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

public static char[] encodeHex( final byte[] data ){
    final int l = data.length;
    final char[] out = new char[l<<1];
    for( int i=0,j=0; i<l; i++ ){
        out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
        out[j++] = HEX_DIGITS[0x0F & data[i]];
    }
    return out;
}

然后你就可以做了

String s = new String( encodeHex(myByteArray) );