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 范围内的字节数据时,推荐使用 UByteArrayubyteArrayOf()

但由于该功能属于实验性 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)
}

要点说明:

  • ✅ 使用 Uu 后缀声明无符号字面量(如 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()

关键点:

💡 小贴士:ByteBuffer 特别适合实现自定义序列化、网络包组装等场景,比手动位运算清晰得多。

6. 总结

本文系统介绍了 Kotlin 中字节数组的创建与拼接方式:

方法 适用场景 是否推荐
byteArrayOf() 创建有符号字节数组 ✅ 常用
ubyteArrayOf() + @OptIn 创建无符号字节数组(0~255) ✅ 处理二进制数据必备
+ 操作符 简单拼接,代码简洁 ⚠️ 小数据量可用
copyInto() 高性能拼接 ✅ 推荐
System.arraycopy JVM 层高效复制 ✅ 推荐
ByteArrayOutputStream 动态追加多段数据 ✅ 易用
ByteBuffer 复合类型写入、协议封装 ✅ 强烈推荐

所有示例代码均已上传至 GitHub,欢迎 clone 学习:

👉 GitHub 源码地址


原始标题:Creating a Byte Array in Kotlin