arraylist.clone()是浅拷贝,因仅用system.arraycopy()复制数组引用,未克隆元素本身;深拷贝需手动遍历并克隆每个元素。

为什么 ArrayList.clone() 不是深拷贝
调用 ArrayList.clone() 返回的是一个新对象,但内部的 elementData 数组引用没变——也就是说,两个 List 共享同一块堆内存。改原 List 的某个可变元素(比如 new ArrayList<string>()</string> 里的子列表),克隆体也会跟着变。
- 这是浅拷贝,
clone()方法底层只调用了System.arraycopy()复制数组引用,不是复制每个元素本身 - 如果你真需要深拷贝,得自己遍历、调用每个元素的
clone()或构造新实例,System.arraycopy()帮不上忙 - 注意:
ArrayList的clone()是protected的,外部得通过反射或继承才能直接调;日常建议用构造器new ArrayList(original),它内部也走System.arraycopy()
System.arraycopy() 在 ArrayList.add() 和扩容时怎么用
每次 add() 超出当前容量,ArrayList 就要扩容:新建更大数组,再把老数据搬过去——这个“搬”就是 System.arraycopy() 干的活。
- 典型调用形如:
System.arraycopy(elementData, 0, newElementData, 0, size) - 它比手动 for 循环快,因为 JVM 对该方法做了 intrinsic 优化(HotSpot 里直接映射到内存拷贝指令)
- 别自己写循环复制数组,尤其在扩容路径上;哪怕只差一两个元素,
System.arraycopy()的常数因子也明显更低 - 注意参数顺序:源数组、源起始索引、目标数组、目标起始索引、长度;错一位就丢数据或越界,常见错误是把
size写成elementData.length
用 System.arraycopy() 手动实现 subList 快照的坑
想截取一段 ArrayList 数据做不可变快照?别直接返回 subList(),它只是视图,原 List 一改它就失效。有人会想手动拷出来:System.arraycopy() 确实能干这事,但容易漏细节。
- 必须先确认源数组不为 null,且
fromIndex、toIndex在合法范围内,否则抛ArrayIndexOutOfBoundsException - 目标数组得提前 new 好,长度是
toIndex - fromIndex;用错长度会导致拷贝不全或越界 - 如果元素类型是泛型
E,目标数组得声明为Object[](因为泛型擦除),再强转——但运行时类型必须匹配,否则ClassCastException在取值时才暴露 - 示例:
Object[] snapshot = new Object[toIndex - fromIndex];<br>System.arraycopy(elementData, fromIndex, snapshot, 0, snapshot.length);
Arrays.copyOf() 和 System.arraycopy() 到底选哪个
Arrays.copyOf() 底层就是封装了一次 System.arraycopy(),但它省了 new 数组这步。多数时候你该用前者,除非你在写高性能容器代码、对每行字节都敏感。
-
Arrays.copyOf(elementData, newLength)自动处理 new 数组 + 拷贝,语义清晰,不易出错 -
System.arraycopy()更底层,适合已知目标数组存在、只需搬运的场景(比如扩容、移动区间) - 性能差异微乎其微,JVM 优化后两者几乎等价;选哪个主要看可读性和控制粒度
- 别为了“看起来更底层”硬用
System.arraycopy()——ArrayList源码里该用Arrays.copyOf()的地方(如toArray())也早就换了
实际写业务代码时,System.arraycopy() 出现场景远少于你以为的那么多;真正关键的是理解它在哪被调用、为什么不能替换成循环、以及它永远不碰元素内容——这点一旦忽略,深拷贝和并发修改的问题就藏不住了。










