如果在java中引用是按值传递的,在哪里将引用设置为null?
下面的代码构造了2个linkedlist,并将它们传递给构造第三个合并的linkedlist。然而,一旦合并,l1和l2(最初构建的)linkedlist就发生了变化。在这段代码中,l1和l2应该在哪里设置为null 编辑:将其设置为null的原因是我们不希望客户端将来使用修改过的l1或l2。我们希望有一个明确的合同,一旦合并,我们有一个新的参考和以前的参考是无效的,以便有人不使用他们认为他们是链接列表时,他们的建设如果在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
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这是一个很好的观点,感谢您指出这一点。不过,把我的答案留在这里,因为我认为一般来说,失效应该在对象级别上进行,而不是在引用级别上,这就是我试图提供的信息。