为何Java内置LinkedList.add()不影响原浅拷贝列表,自定义实现却会?
为什么内置LinkedList与自定义链表的浅拷贝行为不同?
核心差异本质
两者的clone实现完全不是一个层面的操作:
- 自定义
Node1的clone是单个节点的浅拷贝,仅复制当前节点的字段值(包括next引用),并未复制整个链表结构; - Java内置
LinkedList的clone是整个列表容器的浅拷贝,创建了全新的LinkedList实例,同时复制原列表的元素引用,但内部的双向链表节点结构是独立的。
逐个场景拆解
1. 自定义Node1链表的行为分析
你的Node1类继承了Object的clone方法,该方法仅做浅拷贝:
protected Node1 clone() throws CloneNotSupportedException { return (Node1) super.clone(); }
当执行rohit = (Node1)first.clone()时:
rohit是一个新的Node1实例,但它的next字段和原first的next指向同一个Node1对象(即second);- 后续遍历
rohit直到最后一个节点(也就是second),然后执行rohit.next = rohit_add,本质是修改了second的next引用; - 原列表
first的next依然指向second,所以原列表的链表结构被同步修改,出现first -> second -> rohit_add的结果。
2. 内置LinkedList的行为分析
JDK源码中LinkedList.clone()的核心逻辑(简化版)如下:
public Object clone() { LinkedList<E> clone = superClone(); // 初始化新链表的独立头尾节点与尺寸 clone.first = null; clone.last = null; clone.size = 0; // 遍历原链表,将元素引用逐个添加到新链表的独立节点中 for (Node<E> x = first; x != null; x = x.next) clone.add(x.item); return clone; }
当执行l2 = (LinkedList) l1.clone()时:
l2是一个全新的LinkedList对象,拥有独立的first、last节点和size字段;- 新链表会遍历原链表的所有元素,将每个元素的引用(比如
AtomicInteger对象)复制到新创建的Node<E>实例中; - 向
l2添加new AtomicInteger(300)时,只是在l2自己的节点链末尾新增一个节点,完全不会影响原l1的节点结构,因此原列表不会出现该元素。
而你后续修改l1.get(0).incrementAndGet()会影响l2,这才是浅拷贝的典型表现:元素引用共享,但容器结构独立。
如何让自定义链表实现类似LinkedList的clone行为
如果想让自定义链表的拷贝拥有独立的结构(仅共享元素引用),需要实现整个链表的拷贝,而不是单个节点的拷贝:
protected Node1 clone() throws CloneNotSupportedException { Node1 newHead = new Node1(this.data); Node1 current = newHead; Node1 originalNext = this.next; // 遍历原链表,逐个拷贝节点 while (originalNext != null) { current.next = new Node1(originalNext.data); current = current.next; originalNext = originalNext.next; } return newHead; }
这样拷贝后的链表结构完全独立,修改拷贝的链表不会影响原链表。
内容的提问来源于stack exchange,提问作者Rohit Rohra
相关产品推荐
相关产品推荐

