添加新节点时,链表的浅表副本无法反映更改

时间:2019-03-09 18:16:08

标签: java linked-list

diagram 我已经读了很多书,但是好像不问这里就无法消除我的困惑。基于该图,当我使用clone()创建链表的浅表副本时。创建一个新的链表,并将原始变量中head变量的引用值复制到克隆的变量中,并共享其余节点。因此,如果我使用克隆添加新节点,则原始节点应该可以看到它,不是吗?但是当输出列表1时,将省略值3。有人可以告诉我为什么吗?

LinkedList<Integer> list1 = new LinkedList<>();
l1.add(1);
l1.add(2);
LinkedList<Integer> list2 = (LinkedList) l1.clone();
l2.add(3); 

2 个答案:

答案 0 :(得分:1)

clone()创建新的LinkedList结构,并将新引用返回给第一个节点。这两个LinkedList之间的关系是它们共享相同的节点 values 。当您在旧列表或新列表上执行某些add \ remove操作时,这些操作将不会更改其他列表。这就是为什么我们进行copy的原因-更改副本时,我们不想更改原始链表的结构。

摘自LinkedList.clone文档:

  

返回此LinkedList的浅表副本。 (元素本身   未克隆。)   @returnLinkedList实例的浅表副本

请考虑以下示例:

import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

public class LinkedListsApp {

    public static void main(String[] args) throws Exception {
        LinkedList<AtomicInteger> l1 = new LinkedList<>();
        l1.add(new AtomicInteger(100));
        l1.add(new AtomicInteger(200));

        LinkedList<AtomicInteger> l2 = (LinkedList) l1.clone();
        l2.add(new AtomicInteger(300));

        System.out.println(l1);
        System.out.println(l2);

        // change element on first list
        l1.get(0).incrementAndGet();

        System.out.println();
        System.out.println("After change internal state of first element");
        System.out.println(l1);
        System.out.println(l2);
    }
}

上面的代码显示:

[100, 200]
[100, 200, 300]

After change internal state of first element
[101, 200]
[101, 200, 300]

我们可以看到,当我们从第一个列表更改第一个元素的内部状态时,第二个列表也可见。因此,没有每个元素值的深层副本,而是结构的副本-节点和顺序的副本。

为明确起见,让我们看一下Java 8中的实现:

public Object clone() {
    LinkedList<E> clone = superClone();

    // Put clone into "virgin" state
    clone.first = clone.last = null;
    clone.size = 0;
    clone.modCount = 0;

    // Initialize clone with our elements
    for (Node<E> x = first; x != null; x = x.next)
        clone.add(x.item);

    return clone;
}

看看for-each循环。遍历原始列表,并将值添加到clone列表中。方法add创建一个新的Node对象,该对象存储与原始列表相同的值:x.item

答案 1 :(得分:0)

clone()方法创建第一个链表的精确副本,然后在此处将其返回到l2。它的作用是创建一个新实例,并复制原始实例中的所有字段,然后将其返回给l2对象。如果我们想要的只是一个浅表副本,我们可以简单地使用赋值运算符,在这种情况下,对l1所做的任何更改都会反映在l2上,因为不会创建新对象,而只会创建引用的副本。