在java中传递引用的引用在何处设置对null的引用?

时间:2013-06-16 06:30:50

标签: java data-structures linked-list

以下代码构造2个链接列表并传递它们以构建第3个合并链接列表。但是一旦合并,l1和l2(最初构建的)链接列表已经改变。在这个代码中,l1和l2应该设置为null吗?

编辑:将其设置为null的原因是我们不希望客户端在将来使用修改后的l1或l2。我们希望有一个明确的合同,一旦合并,我们有一个全新的参考,以前的参考是无效的,所以有人不会使用他们相信他们是在他们的建设时间链接列表。

public class MergeLinkedList {
    Node first;
    Node last;

    public void add (int val) {
        final Node l = last;
        final Node newNode = new Node(val, null);
        last = newNode;
        if (first == null) {
            first = newNode;
        } else {
            l.next = newNode;
        }
    }

    public void displayList() {
        Node tempFirst = first;
        while (tempFirst != null) {
            System.out.print(tempFirst.item + " ");
            tempFirst = tempFirst.next;
        }
    }

    private static class Node {
        int item;
        Node next;

        Node(int element, Node next) {
            this.item = element;
            this.next = next;
        }
    }

    private Node mergeLinkedListRecursive(Node list1, Node list2) {
        if (list1 == null) {
           return list2; 
        }

        if (list2 == null) {
            return list1;
        }

        if (list1.item < list2.item) {
            list1.next = mergeLinkedListRecursive(list1.next, list2);
            return list1;
        } else {
            list2.next = mergeLinkedListRecursive(list1, list2.next);
            return list2;
        }
    }

    public void mergeLinkedListRecursion(MergeLinkedList list1, MergeLinkedList list2) {
        first = mergeLinkedListRecursive(list1.first, list2.first);
    }



    public static void main(String[] args) {

        int[] a1 = {1, 3, 5};
        int[] a2 = {2, 4};

        MergeLinkedList l1 = new MergeLinkedList();
        for (int val : a1 ) {
            l1.add(val); 
        }

        MergeLinkedList l2 = new MergeLinkedList();
        for (int val : a2) {
            l2.add(val);
        }

        MergeLinkedList l3 = new MergeLinkedList();
        l3.mergeLinkedListRecursion(l1, l2);
        l3.displayList();
    }
}

3 个答案:

答案 0 :(得分:2)

正确使用由l1l2命名的对象的责任在于来电者

也就是说,虽然调用者可能会认为l1l2总是评估为相同的项目序列,但在l3.mergeLinkedListRecursion(l1, l2);之后并非如此。这是因为方法执行副作用并改变对象

在方法调用之后,由调用者决定是否正确使用l1l2 。 “正确”使用可能根本就不使用它们。 (l1l2变量仍然评估为两个列表的头部,一个是子列表;只是没有预期的。)

即使可以将参数(l1l2)分配给null - 即使用call-by-reference语义 - 也不可能确保所有变量/评估所述(现在已变异)对象的字段被指定为空

变量/字段是特定对象的名称:如何跟踪给定对象的所有名称,比如苏打 - 或者是Pop?可乐?可乐? 코카인?

防止此问题的一种方法是使用不可变列表;在这种情况下,合并列表实际上将包含所有节点,而不是交织现有节点 - 也就是说,使用不可变列表,next只能 构造函数。像Haskell和Scala这样的语言遵循这种方法。

另一种方法(我毫不犹豫地建议)是制作“容器容器”;对于链表,它可能只是一个“虚拟头节点”,当在“使其无效”的列表上执行操作时,next将被赋值为null。但是,我发现这是一个难以正确实现的hackish and uneeded解决方案,并且污染了一个简单的可变链接列表实现。

基本问题是突变和副作用,这只是编写非纯代码的丑陋方面之一 - 一点点文档(以及阅读这些文档)可以大有帮助。此外,单元测试:测试可以捕获可能由错误假设引起的问题。

答案 1 :(得分:0)

我不知道你为什么要将l1和l2设置为null,但为了做到这一点,你需要把它放在这一行之后:

l3.mergeLinkedListRecursion(l1, l2);

为什么要将它们设置为null?

答案 2 :(得分:0)

如果Node对象在用作mergeLinkedListRecursive()的参数后变为无效,那么这是节点应该记住的内容:

class Node {
    ...
    boolean valid = true;
    void markInvalid() { valid = false; }
}

然后在mergeLinkedListRecursive()中将它们标记为:

list1.markInvalid();
list2.markInvalid();

当然,让Node的方法首先验证它是否处于有效状态。