1. 概述
本文将深入探讨如何在 Kotlin 中创建字节数组。我们会先了解 Kotlin 中 Byte
类型的表示方式,然后分别演示如何使用有符号和无符号字节来构建字节数组。此外,还会介绍几种常见的字节数组合并技巧——这在处理网络协议、序列化或文件操作时非常实用,稍不注意就容易踩坑。
2. Kotlin 中的字节表示
Kotlin 使用 Byte
类型表示一个字节(8位)。✅ 关键点:Byte
是有符号类型,取值范围为 -128 到 127。这意味着它用最高位表示正负,实际可表示的数据范围受限。
⚠️ 注意:如果你需要表示 0~255 的无符号字节(比如解析图片、二进制协议),就不能直接用 Byte
,否则超过 127 的值会溢出成负数。
为此,Kotlin 提供了实验性支持的无符号类型 UByte
(即 kotlin.UByte
),其取值范围是 0 到 255。不过由于它是实验特性,使用时需显式启用。
相关链接:
3. 使用有符号字节创建字节数组
Kotlin 提供了内置函数 byteArrayOf()
,可直接创建 ByteArray
实例。参数支持十进制、十六进制或二进制字面量,只要在 -128 ~ 127
范围内即可。
示例代码如下:
@Test
fun `create a byte array using signed byte`() {
val byteArray = byteArrayOf(0x48, 101, 108, 108, 111)
val string = String(byteArray)
assertThat(string).isEqualTo("Hello")
}
上面的例子中:
0x48
是'H'
的 ASCII 十六进制101
是'e'
的十进制 ASCII 值- 最终拼成字符串
"Hello"
也可以包含负数:
@Test
fun `create a byte array with negative values`() {
val byteArray = byteArrayOf(Byte.MIN_VALUE, -1, 0, 1, Byte.MAX_VALUE)
assertThat(byteArray)
.hasSize(5)
.containsExactly(-128,-1, 0, 1, 127)
}
❌ 如果传入超出范围的值,例如 128.toByte()
,编译器会报错:
“The integer literal does not conform to the expected type Byte”
这是 Kotlin 类型安全的体现,避免运行时出现意料之外的行为。
4. 使用无符号字节创建字节数组
当需要处理 0~255 范围内的字节数据时,推荐使用 UByteArray
和 ubyteArrayOf()
。
但由于该功能属于实验性 API,必须添加注解启用:
@OptIn(ExperimentalUnsignedTypes::class)
完整示例如下:
@Test
@OptIn(ExperimentalUnsignedTypes::class)
fun `create a byte array using unsigned byte`(){
val uByteArray = ubyteArrayOf(UByte.MIN_VALUE, 130U, 131u, UByte.MAX_VALUE)
val intValues = uByteArray.map { it.toInt() }
assertThat(intValues)
.hasSize(4)
.containsExactly(0, 130, 131, 255)
}
要点说明:
- ✅ 使用
U
或u
后缀声明无符号字面量(如130U
) - ❌ 不允许负数,否则编译失败
- ⚠️ 所有涉及
UByte
的代码都需标注@OptIn
,否则会有警告
虽然目前仍是实验特性,但在处理底层二进制数据(如音视频、加密)时非常有用,建议关注后续稳定化进程。
官方参考:
5. 向 ByteArray 追加字节
实际开发中,经常需要将多个 ByteArray
拼接,或者向已有数组追加新字节。以下是几种常用方法。
5.1. 使用 +
操作符(最简洁)
Kotlin 重载了 +
操作符,支持多种拼接场景:
val byteArray = byteArrayOf(0b00000001, 0b00000010, 0b00000011)
val bytesToAdd = byteArrayOf(0b00000100, 0b00000101)
场景一:两个 ByteArray 拼接
val concatenatedArray = byteArray + bytesToAdd
验证结果:
Assertions.assertEquals(byteArray.size + bytesToAdd.size, concatenatedArray.size)
Assertions.assertEquals(0b00000001, concatenatedArray[0])
Assertions.assertEquals(0b00000010, concatenatedArray[1])
Assertions.assertEquals(0b00000011, concatenatedArray[2])
Assertions.assertEquals(0b00000100, concatenatedArray[3])
Assertions.assertEquals(0b00000101, concatenatedArray[4])
场景二:追加单个字节
val byteToAdd = 0b00000100.toByte()
val concatenatedArray = byteArray + byteToAdd
场景三:追加字节集合
val bytesToAddList = listOf(0b00000100.toByte(), 0b00000101.toByte())
val concatenatedArray = byteArray + bytesToAddList
✅ 优点:语法简洁,适合小规模拼接
❌ 缺点:每次都会创建新数组,频繁操作性能较差
5.2. 使用 copyInto()
方法(高效可控)
若追求性能或需精确控制内存布局,可用 copyInto()
方法手动拷贝:
fun ByteArray.copyInto(
destination: ByteArray,
destinationOffset: Int = 0,
startIndex: Int = 0,
endIndex: Int = size
): ByteArray
实现拼接:
val concatenatedArray = ByteArray(byteArray.size + bytesToAdd.size)
byteArray.copyInto(concatenatedArray) // 默认从 offset=0 开始
bytesToAdd.copyInto(concatenatedArray, destinationOffset = byteArray.size) // 接着写
⚠️ 注意事项:
- 必须预先分配目标数组大小
- 第二次拷贝要指定
destinationOffset
,否则会覆盖前面的数据
✅ 优点:只创建一次数组,效率高
✅ 适用场景:高性能要求、循环拼接等
5.3. 借助 Java 工具类(灵活强大)
Kotlin 与 Java 完全互操作,可以直接使用 Java 标准库中的高效工具。
方式一:System.arraycopy
val concatenatedArray = ByteArray(byteArray.size + bytesToAdd.size)
System.arraycopy(byteArray, 0, concatenatedArray, 0, byteArray.size)
System.arraycopy(bytesToAdd, 0, concatenatedArray, byteArray.size, bytesToAdd.size)
这是 JVM 层面的高效内存复制,底层由 C 实现,性能极佳。
相关文档:Java 数组拷贝机制
方式二:ByteArrayOutputStream
适合动态追加多个片段:
val outputStream = ByteArrayOutputStream()
outputStream.write(byteArray)
outputStream.write(bytesToAdd)
val concatenatedArray = outputStream.toByteArray()
内部原理:toByteArray()
实际调用了 Arrays.copyOf()
,自动扩容。
相关文档:Java Arrays 工具类
方式三:ByteBuffer(推荐用于复杂二进制协议)
当你需要混合写入不同类型数据(如 int、short、byte[])时,ByteBuffer
是最佳选择:
val buffer = ByteBuffer.allocate(byteArray.size + bytesToAdd.size)
buffer.put(byteArray)
buffer.put(bytesToAdd)
val concatenatedArray = buffer.array()
更进一步,还可以写入整数:
val valueToAdd = 256
val buffer = ByteBuffer.allocate(byteArray.size + Integer.BYTES)
buffer.put(byteArray)
buffer.putInt(valueToAdd) // 自动写入 4 个字节
val concatenatedArray = buffer.array()
关键点:
- ✅ 使用
Integer.BYTES
确保空间足够 - ✅
putInt()
按当前字节序写入 4 字节 - 🔗 参考:Java ByteBuffer 官方指南
- 🔗 putInt 详解:/java-bytebuffer#2-transfer-int-data
💡 小贴士:ByteBuffer
特别适合实现自定义序列化、网络包组装等场景,比手动位运算清晰得多。
6. 总结
本文系统介绍了 Kotlin 中字节数组的创建与拼接方式:
方法 | 适用场景 | 是否推荐 |
---|---|---|
byteArrayOf() |
创建有符号字节数组 | ✅ 常用 |
ubyteArrayOf() + @OptIn |
创建无符号字节数组(0~255) | ✅ 处理二进制数据必备 |
+ 操作符 |
简单拼接,代码简洁 | ⚠️ 小数据量可用 |
copyInto() |
高性能拼接 | ✅ 推荐 |
System.arraycopy |
JVM 层高效复制 | ✅ 推荐 |
ByteArrayOutputStream |
动态追加多段数据 | ✅ 易用 |
ByteBuffer |
复合类型写入、协议封装 | ✅ 强烈推荐 |
所有示例代码均已上传至 GitHub,欢迎 clone 学习: