以下代码构造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();
}
}
答案 0 :(得分:2)
正确使用由l1
和l2
命名的对象的责任在于来电者。
也就是说,虽然调用者可能会认为l1
和l2
总是评估为相同的项目序列,但在l3.mergeLinkedListRecursion(l1, l2);
之后并非如此。这是因为方法执行副作用并改变对象。
在方法调用之后,由调用者决定是否正确使用l1
和l2
。 “正确”使用可能根本就不使用它们。 (l1
和l2
变量仍然评估为两个列表的头部,一个是子列表;只是没有预期的。)
即使可以将参数(l1
和l2
)分配给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
的方法首先验证它是否处于有效状态。