我正在使用双端队列进行分配,我们遇到的问题是,在通过极其简单的方法传递后,对象引用从节点中消失。
一些重要的定义:
class Node {
String s;
Node prev;
Node next;
...
}
class Sentinel extends Node {
Node prev;
Node next;
//Constructor uses that of Node
}
class Deque {
Sentinel start;
...
}
我们正在编写的一种方法是根据给定的字符串从双端队列中删除一个节点。
在deque:
public void removeSorted(String toRemove) {
// System.out.println(this.start);
// System.out.println(this.start.next);
this.start.next.removeSorted(toRemove);
}
注释掉的println显示正确的Sentinel和Node。
然后,在Node:
public void removeSorted(String toRemove) {
if (this.s.equals(toRemove)) {
// System.out.println(this.prev);
// System.out.println(this.prev.next);
this.prev.next = this.next;
this.next.prev = this.prev;
} else if (this.s.compareTo(toRemove) > 0) {
throw new RuntimeException("String does not exist in these nodes!");
} else {
this.next.removeSorted(toRemove);
}
}
this.prev
的println按照预期在第一次递归时输出Sentinel。但是,this.prev.next输出null而不是Node。
此功能仅在尝试直接在Sentinel之后删除第一个节点时失败。如果您尝试删除任何其他节点,它可以正常工作,并尝试调用this.prev.next
会导致非空答案。
为什么传递给函数(紧接在后面)时引用会消失,因为我们在调用函数之前已经显示引用是直接存在的?
答案 0 :(得分:0)
您的问题代码错误,或者您在Node
和Sentinel
中都有相同的字段。这意味着,这两者是不同的:
start.next
是Sentinel类的next
字段,隐藏字段,其中包含来自Node类的相同名称。 start.next.prev.next
也是start
的字段,但现在它是Node类的字段,因为您通过Node引用访问它。从Sentinel中删除prev
和next
。实际上删除整个Sentinel,看起来你用来“删除”String s
,这是不可能的,你不能“删除”超类字段。或者如果您需要/想要哨兵,请参阅下面的替代设计。
此外,这也说明了为什么你应该使用 getters 和 setters 而不是直接访问字段...你的IDE可能有很好的重构工具来添加getter等(右击在字段上,请参阅“重构”子菜单),使用它!如果你的IDE没有那个,那就切换到一个(我更喜欢NetBeans,但Eclipse和IntelliJ也值得),在没有这样的IDE的情况下编写Java是一种受虐狂的练习......
另外,在Java中避免这种继承。你应该有这种整体设计:
interface NodeInterface {...}
public class Node implements NodeInterface {...}
public class Sentinel implements NodeInterface {...}
然后在NodeInterface
中定义getter和setter,它们应该作为参数并返回NodeInterface
类型。 Sentinel
类当然不支持所有接口方法,因此这些方法可以return null;
/什么也不做,或throw new IllegalStateException("Sentinel does not support Xxxx.");
取决于方法,如果调用该方法为sentinel是调用代码的bug与否(最好从抛出异常开始)。
如果这是学校工作而你还没有通过接口,那么用interface NodeInterface
(最好是抽象)替换class NodeBase
,但在“现实世界”中这将是错误的代码,因为Java确实如此不支持多重继承。