1. 概述

在本教程中,我们将介绍几种在 Kotlin 中将 ByteArray 转换为十六进制字符串的方法。

我们会先介绍转换的基本算法,然后使用 Kotlin 或 Java 标准库来实现。最后,我们还会介绍一种使用循环和位运算的底层实现方式,帮助理解其底层逻辑。


2. 算法原理

将字节数组转换为十六进制字符串的基本步骤如下:

  1. 将每个字节的无符号值转换为对应的十六进制表示
  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:取低四位

⚠️ 图解:

and

示例:-1 & 0xff 结果为 255

div

示例:255 / 16 = 15,255 % 16 = 15,对应 "ff"


6. 总结

我们介绍了多种在 Kotlin 中将字节数组转换为十六进制字符串的方法:

方法 优点 缺点
joinToString + format 简洁、易读 依赖格式化字符串
无符号类型 更直观 需要实验性注解
Java 17 HexFormat 官方支持、简洁 仅适用于 Java 17+
手动实现 理解底层原理 代码复杂、可读性差

根据项目需求选择合适的方式即可。例如,若使用 Java 17,推荐使用 HexFormat;否则使用 Kotlin 标准库方法即可。

完整代码示例请参考:GitHub 仓库


原始标题:Convert Byte Arrays to Hex Strings in Kotlin