3a5f771c形式的确切十六进制代码。
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(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] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
根据我的一些简单的基准测试(每个数组大小为一百万字节,执行一千次,每个数组大小为256字节,执行一千万次),它比任何其他选择快得多,在长数组上大约快了一半。与我参考的答案相比,在讨论中建议使用位运算符,可以使长数组的时间缩短约20%。(编辑:当我说它比其他选择更快时,我是指在讨论中提供的备选代码。性能与使用非常类似的Commons Codec相当。)
Java 9紧凑字符串版本(2020年):
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
String printHexBinary(byte[]) 和byte[] parseHexBinary(String)。然而,printHexBinary比这个回答中的函数慢了许多(2倍),我检查了源代码,它使用了一个stringBuilder。parseHexBinary使用了一个数组。不过,对于大多数情况来说,它已经足够快了,而且你可能已经有它了。 - maybeWeCouldStealAVanprintHexBinary方法的性能? - kevinarpejavax.xml.bind.DataTypeConverter 将在 Java 11 中被移除。 - The Impalerint v = bytes[j] & 0xFF;中,为什么需要加上& 0xFF?我有什么遗漏吗?还是说这只是不必要的? - AlbAtNfApache Commons Codec库提供了一个Hex类,可以用来完成这种工作。
import org.apache.commons.codec.binary.Hex;
String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );
import org.apache.commons.codec.binary.Hex;代替import org.apache.commons.codec.*;。 - cytinusorg.bouncycastle.util.encoders.Hex,其中包含这个方法:String toHexString(byte[] data)。 - Guillaume Husta Java Architecture for XML Binding (JAXB) 中的方法 javax.xml.bind.DatatypeConverter.printHexBinary() 是将 byte[] 转换为十六进制字符串的便捷方式。DatatypeConverter 类还包括许多其他有用的数据操作方法。
javax.xml.bind不存在了,如果你想使用包含DatatypeConverter的JAXB,你需要从Maven安装JAXB API和JAXB Runtime。
使用示例:
byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);
将导致:
000086003D
这个答案与这个相同。
最简单的解决方案,没有外部库,没有数字常量:
public static String byteArrayToHex(byte[] a) {
StringBuilder sb = new StringBuilder(a.length * 2);
for(byte b: a)
sb.append(String.format("%02x", b));
return sb.toString();
}
以下是从简单(一行代码)到复杂(大型库)排序的一些常见选项。如果您对性能感兴趣,请参见下面的微基准测试。
一个非常简单的解决方案是使用BigInteger的十六进制表示:
new BigInteger(1, someByteArray).toString(16);
注意,由于它处理的是数字而不是任意字节串,因此它会省略前导零 - 这可能符合您的要求(例如对于3字节输入:000AE3 vs 0AE3)。与选项2相比,这也非常慢,大约慢100倍。
使用%X占位符,String.format()可以将大多数基本类型(short、int、long)编码为十六进制:
String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());
如果您只有4字节数组,您可以使用Integer类的toHexString方法:
Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());
对于8字节数组和Long同样适用。
Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());
JDK 17最终提供了直观的十六进制编码的一级支持,使用HexFormat:
HexFormat hex = HexFormat.of();
hex.formatHex(someByteArray)
以下是一个完整且可复制粘贴的代码片段,支持大写/小写和字节序。它被优化以最小化内存复杂度并最大化性能,并应该与所有现代Java版本(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);
}
完整的源代码和解码器可以在此处找到,使用Apache v2许可证。
在之前的项目中,我创建了这个用于Java字节操作的小工具包。它不需要外部依赖,与Java 7+兼容。其中包括一个非常快速和经过充分测试的HEX编解码器:
import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()
你可以在 GitHub: bytes-java 上查看它。
当然,还有那些优秀的“老派”共用编解码器(commons codecs)。(警告:以下是个人观点)在上面概述的项目中,我分析了这段代码并感到相当失望。有很多重复的、无组织的代码,过时和奇特的编解码器可能只适用于少数人,以及非常过度工程化和缓慢的流行编解码器实现(尤其是Base64)。因此,如果你想使用它或其他替代品,我会做出明智的决定。无论如何,如果你仍然想使用它,这里是一段代码片段:
import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));
很多时候,您已经将Guava作为依赖项。如果是这样,只需使用以下内容:
import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);
如果你使用Spring框架和Spring Security,你可以使用以下方法:
import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));
如果您已经使用安全框架Bouncy Castle,您可以使用其Hex工具:
import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);
在之前的Java(8及以下)版本中,JAXB的Java代码作为运行时依赖项包含在其中。自Java 9和Jigsaw模块化以来,您的代码无法在未经明确声明的情况下访问其模块之外的其他代码。因此,如果您收到类似于以下异常:
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
如果在Java 9+的JVM上运行,请切换到上述任何一种替代实现。另请参见此问题。
以下是一个简单JMH微基准测试编码不同尺寸的字节数组的结果。这些值是每秒操作次数,因此较高的值更好。请注意,微基准测试往往不能代表真实世界的行为,因此请对这些结果持怀疑态度。
| 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 |
规格:JDK 8u202,i7-7700K,Win10,24 GB Ram。查看完整基准测试此处。
以下是使用当前的JMH 1.36、Java 17和更高端的计算机的结果。
| Name (ops/s) | 16 byte | 32 byte | 128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1a: BigInteger | 2,941,403 | 1,389,448 | 242,096 | 5 |
| Opt1d: HexFormat | 32,635,184 | 20,262,332 | 7,388,135 | 922 |
| Opt2/3: Bytes Lib | 31,724,981 | 22,786,906 | 6,197,028 | 930 |
规格: JDK Temurin 17.0.6,Ryzen 5900X,Win11,24GB DDR4内存
HexFormat.of添加到使用JDK17+的选项中。 - automorphic一种完整的番石榴解决方案:
import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);
现在 hex 是 "48656c6c6f20776f726c64"。
new HashCode(bytes).toString()。 - mfulton26HashCode.fromBytes(checksum).toString()。 - Devstr这个简单的一行代码对我很有用:
String result = new BigInteger(1, inputBytes).toString(16);
编辑-使用此代码将删除前导零,但它适用于我的用例。感谢@Voicu指出
Java 17最终包含了HexFormat类,因此您可以轻松地执行以下操作:
HexFormat.of().formatHex(bytes);
它支持将配置设置为小写/大写、分隔符、前缀、后缀等。
如果是固定长度的内容,比如哈希值,我会使用类似这样的方式:
md5sum = String.format("%032x", new BigInteger(1, md.digest()));
使用 DataTypeConverter 类javax.xml.bind.DataTypeConverter
String hexString = DataTypeConverter.printHexBinary(bytes[] raw);
HexFormat.of().formatHex(bytes)- Ali Dehghani