我在PHP工作2年后回到Java。 对不起,如果这看起来很傻:
这是代码(图表的深度优先遍历):
public List<List<Edge>> paths = new ArrayList<>();
public void traverse(Edge edge, List<Edge> currentPath){
String vertex = graph.getEdgeTarget(edge);
if(edge!=null) currentPath.add(edge);
if(vertex=="TARGET_VERTEX"){
System.out.println(currentPath); // prints fine
paths.add(currentPath); // elements are empty at end of reccursion
if(edge!=null) currentPath.remove(edge);
return;
}
for(Edge e : graph.outgoingEdgesOf(vertex)){
traverse(e, currentPath);
}
if(edge!=null) path.remove(edge);
}
public void search(){
//graph is initalized, vertices and edges are added
for(Edge e : graph.outgoingEdgesOf("START_VERTEX")){
traverse(e, new ArrayList<Edge>());
}
System.out.println("############################");
System.out.println(paths);
System.out.println(paths.size());
}
有人可以解释为什么在递归结束时paths
有空元素,以及如何使其包含我需要的路径?
似乎通过引用传递使我成为一个问题...
ArrayList
有一个浅clone()
方法,不会复制元素(按照JavaDoc)。
我是否需要创建一个临时变量,它将手动复制currentPath
(迭代值)?
我仍然对通过Java传递值和引用感到困惑,在PHP中可以通过引用传递(&variable
来轻松区分。)
感谢。
已编辑,因此我不会抱怨字符串比较
答案 0 :(得分:1)
第if(edge!=null) currentPath.remove(edge);
行删除了List
的元素。
这可能会导致您的问题,因为currentPath
会被递归。
关于不相关的问题,您要将String
与==
进行比较,而不是使用equals
,这是不好的做法。 (有关更多说明,请参阅here)。
答案 1 :(得分:1)
这两行造成了问题:
paths.add(currentPath);
currentPath.remove(edge);
将currentPath
添加到paths
时,会添加currentPath
的引用。最后,currentPath
为空,因此paths
留有空引用。
要避免此问题,请创建currentPath
的副本,然后在paths
中添加副本。
同时更新以下行:
if(vertex=="TARGET_VERTEX"){
as
if("TARGET_VERTEX".equals(vertex)){
使用正确的字符串相等性检查并避免NullPointerException。
如果您想忽略检查的情况,请使用equalsIgnoreCase()
方法。
答案 2 :(得分:1)
您正在添加和删除路径。它似乎错误地做了,你可以尝试调试应用程序。
Java是按值传递的。这意味着像
这样的方法public void myMethod(MyObject instance) {...}
收到instance
引用值的副本。如果在你做的方法中
instance.setField(newValue);
然后您正在访问您传递的同一对象,因为该引用具有相同的值。但是,如果您在方法
中执行此操作instance = new MyObject(newValue);
然后用于调用方法的对象将保持不变。发生这种情况是因为您更改了副本中的引用值,而不是原始
中的值您可以在javadude详细了解它。
最后,您应该使用.equals方法而不是使用==来比较字符串和其他对象。 This answer should help you
对你的代码进行鸟瞰,我会纠正它(没试过): 通过 为字符串常量创建常量。
//These two lines, minor improvements
public static final String TARGETV= "TARGET_VERTEX";
public static final String STARTV= "START_VERTEX";
更改
if(vertex=="TARGET_VERTEX"){
到
if(vertex.equals(TARGETV)){
关于打印paths变量,System.out.println将打印一个String,您正在传递一个Object(边缘列表列表)。每个对象都有一个toString()方法,当需要Object作为String时,该方法会自动调用。正如文档中所述,默认情况下:
Object类的toString方法返回一个由。组成的字符串 对象是实例的类的名称,at-sign 字符“@”,以及散列的无符号十六进制表示 对象的代码。
所以你可以:
创建一个新类(在内部实现List<List<Edge>>
并覆盖toString()
方法)或您可以实现如下方法:
public static String printPath(List<List<Edge>> paths){
StringBuffer sb = new StringBuffer();
for(List<Edge> le : paths){
for(Edge e: le){
sb.append(le); //or similar method to print edges to String
}
}
return sb.toString();
}
而不是:
System.out.println(paths);
这样做:
System.out.println(printPaths(paths));
答案 3 :(得分:0)
您需要知道的第一件事是对象不是Java中的值。 Java中唯一的类型是基本类型和引用类型,因此Java中唯一的值是基元和引用。 “引用”是指向Object的指针。代码中的paths
,currentPath
,edge
等等都是引用。 paths
和currentPath
的元素也是引用。分配或传递引用时,您将获得指向同一对象的另一个引用。基本上只有在执行new ...
时才能创建新对象。
因此,应该更加明显地发生了什么。在代码中创建边缘列表的唯一位置是search()
函数,当它调用traverse()
函数时。 traverse()
函数不包含任何对象创建表达式。因此,在所有递归和所有这些中,它正在使用并修改相同的列表对象。每次调用traverse()
都会添加然后删除一个元素。因此,在递归结束时,列表中将没有元素。这是您添加到paths
的引用的列表,因此您最后会在paths
中看到对空列表的引用。
你说你在PHP工作过。在PHP5中,对象以相同的方式工作 - 对象不是PHP中的值,它们只能通过指向对象的指针来操作。但是,PHP中的数组(不是对象)是不同的; PHP中的数组是值,因此在分配或传递时,数组将被复制。