如果在java中引用是按值传递的,在哪里将引用设置为null?

如果在java中引用是按值传递的,在哪里将引用设置为null?,java,data-structures,linked-list,Java,Data Structures,Linked List,下面的代码构造了2个linkedlist,并将它们传递给构造第三个合并的linkedlist。然而,一旦合并,l1和l2(最初构建的)linkedlist就发生了变化。在这段代码中,l1和l2应该在哪里设置为null 编辑:将其设置为null的原因是我们不希望客户端将来使用修改过的l1或l2。我们希望有一个明确的合同,一旦合并,我们有一个新的参考和以前的参考是无效的,以便有人不使用他们认为他们是链接列表时,他们的建设 public class MergeLinkedList { Node

下面的代码构造了2个linkedlist,并将它们传递给构造第三个合并的linkedlist。然而,一旦合并,l1和l2(最初构建的)linkedlist就发生了变化。在这段代码中,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();
    }
}
公共类合并链接列表{
节点优先;
最后一个节点;
公共无效添加(int val){
最终节点l=最后一个;
最终节点newNode=新节点(val,null);
last=newNode;
if(first==null){
第一个=新节点;
}否则{
l、 next=newNode;
}
}
公共void显示列表(){
节点tempFirst=first;
while(tempFirst!=null){
System.out.print(tempFirst.item+“”);
tempFirst=tempFirst.next;
}
}
私有静态类节点{
国际项目;
节点下一步;
节点(int元素,下一个节点){
this.item=元素;
this.next=next;
}
}
私有节点mergeLinkedListRecursive(节点列表1、节点列表2){
if(list1==null){
返回列表2;
}
if(list2==null){
返回列表1;
}
如果(列表1.item<列表2.item){
list1.next=mergeLinkedListRecursive(list1.next,list2);
返回列表1;
}否则{
list2.next=mergeLinkedListRecursive(list1,list2.next);
返回列表2;
}
}
public void MergeLinkedList递归(MergeLinkedList列表1、MergeLinkedList列表2){
first=mergeLinkedListRecursive(list1.first,list2.first);
}
公共静态void main(字符串[]args){
int[]a1={1,3,5};
int[]a2={2,4};
MergeLinkedList l1=新的MergeLinkedList();
用于(内部值:a1){
l1.添加(val);
}
MergeLinkedList l2=新的MergeLinkedList();
对于(int val:a2){
l2.添加(val);
}
MergeLinkedList l3=新的MergeLinkedList();
l3.合并链接列表递归(l1,l2);
l3.displayList();
}
}

我不知道为什么要将l1和l2设置为null,但要执行此操作,需要将其放在此行之后:

l3.mergeLinkedListRecursion(l1, l2);

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

如果一个
节点
对象在用作
mergeLinkedListRecursive()
的参数后变得无效,那么节点应该记住这一点:

class Node {
    ...
    boolean valid = true;
    void markInvalid() { valid = false; }
}
然后在
mergeLinkedListRecursive()
中将它们标记为:

list1.markInvalid();
list2.markInvalid();
当然,首先要让
节点的方法验证它是否处于有效状态。

正确使用由
l1
l2
命名的对象的责任在于调用方

也就是说,调用方可能假设
l1
l2
总是在
l3.mergeLinkedListRecursion(l1,l2)之后对相同的项目序列求值情况并非如此。这是因为该方法会产生副作用并对对象进行变异

方法调用后,由调用方确保正确使用
l1
l2
。“正确”的用法可能是根本不使用它们。(
l1
l2
变量的计算结果仍然是两个列表的开头,其中一个是子列表;而不是预期的。)

即使可以将参数(
l1
l2
)赋值为null(即通过引用调用语义),也不可能确保对所述(现在已变异)对象求值的所有变量/字段都赋值为null

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

防止这个问题的一种方法是使用不可变列表;在这种情况下,合并列表实际上将包含所有新节点,而不是将现有节点交织在一起——也就是说,对于不可变列表,
next
只能在构造函数中设置。一些语言,如Haskell和Scala,遵循这种方法

另一种方法(我不太愿意建议)是制作一个“容器”;对于链表,它可能只是一个“虚拟头节点”,当对列表执行“使其无效”的操作时,其
next
将被分配为null。然而,我发现这是一个不受欢迎的黑客解决方案,很难正确实现,并且会弄脏一个简单的可变链表实现


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

通过将l1和l2设置为null,您希望实现什么?在许多情况下(以及在提供的数据上),这将失败,因为相同的节点发生了变异。结果将是
l3.valid->false
@user2246674这是一个很好的观点,感谢您指出这一点。不过,把我的答案留在这里,因为我认为一般来说,失效应该在对象级别上进行,而不是在引用级别上,这就是我试图提供的信息。