杰瑞科技汇

Java byte 数组如何高效复制?

核心方法概览

方法 描述 优点 缺点 适用场景
System.arraycopy() 性能最高的底层方法,用于在数组之间复制数据。 速度极快,是所有方法中最优的。 语法稍显冗长,需要指定源数组、起始位置、目标数组、起始位置和长度。 性能要求极高的场景,如大文件处理、高频网络通信等。
Arrays.copyOf() java.util.Arrays 提供的工具方法,可以复制数组并可选择性地调整新数组长度。 语法简洁,代码可读性好。 内部实现也是 System.arraycopy(),性能同样优秀。 大多数日常开发场景,推荐首选。
Arrays.copyOfRange() java.util.Arrays 提供的工具方法,用于复制数组的一个子集。 语法简洁,专门用于复制指定范围。 性能同 copyOf() 当你只需要复制数组的一部分时,非常方便。
手动循环复制 使用 for 循环或 for-each 循环逐个元素复制。 灵活性高,逻辑直观,适合初学者理解。 性能最差,代码冗长,容易出错。 仅用于学习目的,或需要在复制过程中对每个元素进行特殊处理时。
ByteBuffer 使用 NIO (java.nio.ByteBuffer) 进行复制。 功能强大,可以与 NIO 的其他组件(如通道)无缝集成。 对于简单的数组复制,显得有些“重”,代码量稍多。 在 NIO 网络编程或文件 I/O 场景中,需要将字节数组与缓冲区互转时。

详细代码示例

假设我们有以下原始数组:

Java byte 数组如何高效复制?-图1
(图片来源网络,侵删)
byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

System.arraycopy() - 性能之王

这是最底层、最高效的方法,它直接在内存块之间进行数据拷贝。

byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 创建一个和源数组一样大的目标数组
byte[] destArray = new byte[srcArray.length];
// 调用 System.arraycopy
// 参数: 源数组, 源起始索引, 目标数组, 目标起始索引, 复制长度
System.arraycopy(srcArray, 0, destArray, 0, srcArray.length);
// 验证结果
System.out.println(Arrays.toString(destArray)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

特点:

  • 性能:毋庸置疑,这是最快的。
  • 灵活性:可以指定源数组和目标数组的任意起始位置,以及任意长度。
  • 注意:如果目标数组长度不够,会抛出 ArrayIndexOutOfBoundsException

Arrays.copyOf() - 简洁首选

这是 java.util.Arrays 类提供的一个便捷方法,非常常用。

import java.util.Arrays;
byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 创建一个新数组,其内容是 srcArray 的副本,长度与 srcArray 相同
byte[] destArray = Arrays.copyOf(srcArray, srcArray.length);
// 验证结果
System.out.println(Arrays.toString(destArray)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

特点:

Java byte 数组如何高效复制?-图2
(图片来源网络,侵删)
  • 简洁性:一行代码搞定,非常清晰。

  • 长度灵活性:如果新长度大于原数组,多余的元素会被填充为 0 (byte 的默认值),如果新长度小于原数组,则只复制前面的元素。

    // 复制前 5 个元素
    byte[] shortCopy = Arrays.copyOf(srcArray, 5);
    System.out.println(Arrays.toString(shortCopy)); // 输出: [1, 2, 3, 4, 5]
    // 复制并填充 0
    byte[] longCopy = Arrays.copyOf(srcArray, 15);
    System.out.println(Arrays.toString(longCopy)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0]
  • 性能:其内部实现就是 System.arraycopy(),所以性能和 System.arraycopy() 一样好。


Arrays.copyOfRange() - 精准截取

当你需要复制数组的一部分时,这个方法非常方便。

import java.util.Arrays;
byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 复制从索引 2 (包含) 到索引 6 (不包含) 的元素
byte[] subArray = Arrays.copyOfRange(srcArray, 2, 6);
// 验证结果
System.out.println(Arrays.toString(subArray)); // 输出: [3, 4, 5, 6]

特点:

  • 精准性:专门用于复制一个子数组,左闭右开区间 [from, to)
  • 长度自动计算:新数组的长度是 to - from,无需手动计算。
  • 性能:同样基于 System.arraycopy(),性能优秀。

手动循环复制 - 不推荐用于生产

这种方式最原始,不推荐在实际项目中使用,除非有特殊需求。

import java.util.Arrays;
byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
byte[] destArray = new byte[srcArray.length];
// 使用 for 循环
for (int i = 0; i < srcArray.length; i++) {
    destArray[i] = srcArray[i];
}
// 验证结果
System.out.println(Arrays.toString(destArray)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

特点:

  • 性能差:循环有开销,逐个赋值比内存块拷贝慢得多。
  • 代码冗长:需要手动管理索引,容易出错。
  • 适用性:仅用于教学,或者在循环中需要对每个元素进行复杂逻辑处理时。

ByteBuffer - NIO 场景下的选择

在处理网络或文件 I/O 时,ByteBuffer 是一个核心类,它提供了复制 byte 数组的方法。

import java.nio.ByteBuffer;
import java.util.Arrays;
byte[] srcArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1. 将 byte 数组包装到 ByteBuffer 中
ByteBuffer srcBuffer = ByteBuffer.wrap(srcArray);
// 2. 创建一个新的 ByteBuffer,容量相同
ByteBuffer destBuffer = ByteBuffer.allocate(srcBuffer.capacity());
// 3. 将 srcBuffer 的内容复制到 destBuffer
//    注意:这会复制 srcBuffer 的当前位置到末尾的所有数据
//    srcBuffer 的 position 不为 0,可能需要先重置
srcBuffer.rewind(); // 将 position 设置为 0
destBuffer.put(srcBuffer);
// 4. 从 ByteBuffer 中获取字节数组
byte[] destArray = destBuffer.array();
// 验证结果
System.out.println(Arrays.toString(destArray)); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

特点:

  • NIO 集成:在 NIO 生态中非常方便,可以直接用于 SocketChannelFileChannel
  • 功能丰富ByteBuffer 提供了缓冲区的概念(position, limit, capacity),功能比单纯的数组强大。
  • “重量级”:对于简单的数组复制,创建 ByteBuffer 对象的开销比前几种方法大。

总结与最佳实践

场景 推荐方法 理由
通用、日常开发 Arrays.copyOf() 代码简洁、可读性好,性能卓越,是首选。
需要复制数组的一部分 Arrays.copyOfRange() 语法清晰,专为子数组复制设计。
性能要求极致 System.arraycopy() 底层最快,但代码稍显复杂,在性能瓶颈分析时可以考虑。
NIO 编程 ByteBuffer 与 NIO 管道和通道无缝对接,是 NIO 生态的标准做法。
学习或特殊处理 手动循环 便于理解复制过程,或需要在复制时对每个元素进行额外操作。

最终建议:

在 99% 的情况下,直接使用 Arrays.copyOf()Arrays.copyOfRange() 是最佳选择,它们提供了最佳的性能和代码可读性的平衡。

分享:
扫描分享到社交APP
上一篇
下一篇