2013-07-23 166 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中的數組是數值,因此在分配或傳遞時,數組將被複制。