2013-07-23 119 views
1

我在PHP中工作2年后返回Java。 很抱歉,如果这似乎愚蠢: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。)

感谢。

编辑,以便我不会抱怨字符串比较

+5

Java是*通过值*。期。你也没有正确比较字符串。 –

+0

http://stackoverflow.com/questions/40480/is-java-pass-by-reference – assylias

回答

1

线if(edge!=null) currentPath.remove(edge);删除您List的元素。

这可能会导致您的问题,因为currentPath被递归。

在一个不相关的问题上,您正在比较String==,而不是使用equals,这是不好的做法。 (有关更多说明,请参阅here)。当您添加到currentPathpaths,它加入currentPath参考

 paths.add(currentPath); 
     currentPath.remove(edge); 

1

这两行导致该问题。最后,currentPath为空,因此paths保留空引用。

为避免此问题,请创建currentPath的副本并在paths中添加副本。

而且更新以下行:

if(vertex=="TARGET_VERTEX"){ 

if("TARGET_VERTEX".equals(vertex)){ 

使用正确的字符串平等检查,避免NullPointerException异常。

如果您想忽略该案件,然后使用equalsIgnoreCase()方法。

+0

我仍然无法理解这种传递值/传递引用(我知道每个东西都做什么,但这种递归对我来说有点奇怪)...无论如何,正如我所说的,是否需要像这样手动迭代(因为格式化而产生半伪)'List tmp;对于(我在currentPath中){tmp.add(i)}; paths.add(tmp);'?至于字符串检查,我责怪PHP和我从Java中缺席:) – ekstrakt

+0

我没有看到递归的任何重大复杂因素与传递方式有关,什么是混淆?是否需要打印变量? –

+0

@ChristianVielma这是关于获取连接两个顶点的所有简单路径的列表。它们在递归中被检查。 – ekstrakt

1

您正在添加和从路径中删除。它似乎是做错了,你可以尝试调试应用程序。

Java是通过值。这意味着像

public void myMethod(MyObject instance) {...} 

的方法接收基准的值的副本到instance。如果在你做的方法内

instance.setField(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)){ 

关于打印可变的路径,会的System.out.println打印一个字符串,你传递一个对象(列表的列表边缘)。每个对象都有一个toString()方法,该方法在需要Object作为String时自动调用。如在该文档中,默认情况下表示:

Object类的toString方法返回由 其中物体是一个实例,所述-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)); 
+0

的**引用**添加/删除工作正常。我只需要变量的当前内容(通过值传递)。 – ekstrakt

+0

啊,这是一个不同的事情:你必须实现一个toString方法或打印它自己。 System.out.prinln(路径)将打印变量的值。 –

+0

@SotiriosDelimanolis,这就是我所说的。它是按值传递的,但对象变量实际上是引用。所以如果你通过值传递引用,当你修改引用的内容时,事实上你已经通过引用传递了真实的对象。 –

0

您需要知道的第一件事是对象不是Java中的值。 Java中唯一的类型是基元类型和引用类型,所以Java中唯一的值是基元和引用。 “引用”是指向对象的指针。 pathscurrentPathedge等,在你的代码都是引用pathscurrentPath的元素也是参考文献。当您分配或传递引用时,您会得到指向同一对象的另一个引用。基本上只有当你做new ...时才能创建一个新对象。

因此,从这应该变得更加明显是怎么回事。代码中唯一创建边列表的地方是search()函数,当它调用traverse()函数时。 traverse()函数不包含任何对象创建表达式。所以在所有的递归和所有这一切,它正在和修改相同的列表对象。每次调用traverse()都会添加并删除元素。所以在递归结束时,列表中没有元素。这是与您添加到paths的参考文献相同的列表,因此您当然会在末尾看到对paths中空列表的参考。

你说你在PHP工作。在PHP5中,对象以相同的方式工作 - 对象不是PHP中的值,只能通过指向对象的指针操纵。但是,PHP中的数组(不是对象)是不同的; PHP中的数组是数值,因此在分配或传递时,数组将被复制。