1. 概述
在本教程中,我们将介绍几种在 Kotlin 中将 ByteArray
转换为十六进制字符串的方法。
我们会先介绍转换的基本算法,然后使用 Kotlin 或 Java 标准库来实现。最后,我们还会介绍一种使用循环和位运算的底层实现方式,帮助理解其底层逻辑。
2. 算法原理
将字节数组转换为十六进制字符串的基本步骤如下:
- 将每个字节的无符号值转换为对应的十六进制表示
- 将所有转换后的十六进制字符串拼接起来
由于每个十六进制字符表示 4 位(bit),一个字节(8 位)应对应两个十六进制字符。因此,如果某个字节的十六进制表示只有一位,我们需要在前面补零,以确保解码时能正确还原原始数据。
3. Kotlin 标准库方法
3.1. 使用 joinToString
和格式化字符串
Kotlin 的 joinToString
方法允许我们对 ByteArray
中的每个字节进行处理并拼接结果:
fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
这里使用了 "%02x"
格式化字符串,确保每个字节输出两位十六进制字符,并自动补零。
✅ 示例测试:
val md5 = MessageDigest.getInstance("md5")
md5.update("baeldung".toByteArray())
val digest: ByteArray = md5.digest()
assertEquals("9d2ea3506b1121365e5eec24c92c528d", digest.toHex())
3.2. 使用无符号类型(Kotlin 1.3+)
从 Kotlin 1.3 开始,我们可以使用无符号类型来简化转换:
@ExperimentalUnsignedTypes
fun ByteArray.toHex2(): String = asUByteArray().joinToString("") { it.toString(radix = 16).padStart(2, '0') }
该方法更直观,使用 toString(radix = 16)
转换为十六进制,并通过 padStart
补零。
⚠️ 注意:虽然无符号类型在 Kotlin 1.5 后已稳定,但无符号数组仍处于实验阶段,需要加上 @ExperimentalUnsignedTypes
注解。
如果不希望使用实验性 API,也可以用 Java 的 Byte.toUnsignedInt
方法:
fun ByteArray.toHex3(): String = joinToString("") {
java.lang.Byte.toUnsignedInt(it).toString(radix = 16).padStart(2, '0')
}
4. Java 17 方法
从 Java 17 开始,引入了 HexFormat
工具类,推荐用于字节数组与十六进制之间的转换:
val hex = HexFormat.of().formatHex(digest)
assertEquals("9d2ea3506b1121365e5eec24c92c528d", hex)
✅ 优点:简洁、高效、可读性强,且是官方推荐方式。
5. 手动实现(使用循环和位运算)
为了更深入理解底层原理,我们也可以手动实现转换逻辑:
val hexChars = "0123456789abcdef".toCharArray()
fun ByteArray.toHex4(): String {
val hex = CharArray(2 * this.size)
this.forEachIndexed { i, byte ->
val unsigned = 0xff and byte.toInt()
hex[2 * i] = hexChars[unsigned / 16]
hex[2 * i + 1] = hexChars[unsigned % 16]
}
return hex.joinToString("")
}
5.1. 位运算说明
0xff and byte.toInt()
:将字节转为无符号整数(处理负数)unsigned / 16
:取高四位unsigned % 16
:取低四位
⚠️ 图解:
示例:
-1 & 0xff
结果为 255
示例:255 / 16 = 15,255 % 16 = 15,对应 "ff"
6. 总结
我们介绍了多种在 Kotlin 中将字节数组转换为十六进制字符串的方法:
方法 | 优点 | 缺点 |
---|---|---|
joinToString + format |
简洁、易读 | 依赖格式化字符串 |
无符号类型 | 更直观 | 需要实验性注解 |
Java 17 HexFormat |
官方支持、简洁 | 仅适用于 Java 17+ |
手动实现 | 理解底层原理 | 代码复杂、可读性差 |
根据项目需求选择合适的方式即可。例如,若使用 Java 17,推荐使用 HexFormat
;否则使用 Kotlin 标准库方法即可。
完整代码示例请参考:GitHub 仓库