system.arraycopy() 可高效实现环形缓冲区的覆盖式左移:先计算新 head 和剩余长度,若数据连续则单次拷贝到头部,否则分两段拷贝合并,最后更新 head=0、size=remaining。

System.arraycopy() 本身不直接支持“原地左移”,但可以借助它分段拷贝,配合数组长度和移动步数,高效实现环形缓冲区的覆盖式左移(即丢弃左边若干元素,后续元素前移,空出尾部位置供新数据写入)。
理解目标行为:左移 k 位 ≈ 环形缓冲区“消费”k 个元素
假设有一个容量为 capacity 的字节数组 buf,当前有效数据从索引 head 开始、共 size 个元素(逻辑上是环形,但物理存储是线性数组)。所谓“左移 k 位”,本质是:
- 丢弃最老的 k 个元素(即从 head 开始的前 k 个)
- 剩余 size−k 个元素整体前移 k 位(逻辑上),腾出末尾 k 个位置
- head 更新为
(head + k) % capacity,size 减为size - k
注意:这里“原地左移”不是指在同一个数组上逐个赋值移动(那样 O(n) 且易错),而是用 arraycopy 把待保留段一次性搬到位,避免循环赋值。
关键:分两段拷贝 —— 处理环形跨越
因为 head + size 可能超过数组边界(环形),待保留的数据可能分布在两块不连续区域:
• 第一段:从 head + k 到数组末尾(如果未越界)
• 第二段:从数组开头到 (head + k + remaining) % capacity − 1(如果发生环绕)
但更简单稳妥的做法是——**不真正“移动”,而是调整 head 和 size,并用 arraycopy 把后续数据“拉直”到头部**(适用于需要物理紧凑布局的场景,比如对外暴露一个连续 buffer):
- 若
head + k + (size − k) ≤ capacity(即剩余数据没跨边界):
直接System.arraycopy(buf, head + k, buf, 0, size − k) - 否则(跨边界):
先拷贝后半段:System.arraycopy(buf, head + k, buf, 0, capacity − (head + k))
再拷贝前半段:System.arraycopy(buf, 0, buf, capacity − (head + k), (size − k) − (capacity − (head + k)))
完成后设 head = 0,size = size − k。这样 buf[0..size−1] 就是连续的有效数据。
实用示例:固定大小环形缓冲区的 consume() 方法
以下是一个精简版实现,模拟消费 k 个字节并整理为紧凑前缀:
public class CircularBuffer {
private final byte[] buf;
private int head; // 当前有效数据起始索引
private int size; // 当前有效数据长度
<pre class="brush:java;toolbar:false;">public CircularBuffer(int capacity) {
this.buf = new byte[capacity];
this.head = 0;
this.size = 0;
}
// 消费 k 个字节:左移 k 位,丢弃最老数据
public void consume(int k) {
if (k <p>}</p>注意事项与优化提示
- 避免无谓拷贝:纯环形语义下,只要维护好 head 和 size,完全无需移动数据——左移只是更新 head。只有当外部需要“物理连续 buffer”(如 write() 到 OutputStream)时,才触发 arraycopy 整理
- 边界检查不可少:k 超过当前 size 时应截断,防止越界异常
- 性能对比:arraycopy 是 JVM 内置优化的本地方法,比 Java 循环快得多;但频繁整理仍影响性能,建议批量 consume 或按需整理
- 线程安全:该实现非线程安全;如多线程读写,需额外同步或使用 java.util.concurrent 类











