1

I have a recursive method like so:

class MyClass {
ArrayList<ArrayList<MyNode> allSeriesOfNodes = new ArrayList<ArrayList<MyNode>();

public void myMethod(MyNode currNode, ArrayList<MyNode> nodes) {
    // make sure currNode and nodes aren't null, blah....
    nodes.add(currNode);
    for(MyNode n: otherNodeList) {
        for(listItem in n.getList()) {
            if(...) {
                myMethod(n, nodes);
        }
    }
if(currNode is a leaf and nodes is > 1) {
    allSeriesOfNodes.add(nodes);
}

Debugging it I noticed when a stack frame pops and the previous stackframe resumes, the nodes list still has the value of currNode although it popped off.

nodeslist is a local variable just like currNode, and the recursion should remember the state of nodelist per stack frame. Returning to the previous stackframe, why isn't nodelist A instead of A, B (myMethod(B, nodes) was popped) .

HukeLau_DABA
  • 2,384
  • 6
  • 33
  • 51
  • 3
    Where are you "popping" values from `nodes`? – Brett Okken Aug 03 '14 at 22:49
  • 3
    I think your code example is too incomplete for us to answer. Although I'll point that currNode is **NOT** a local variable, but a parameter, and therefore is not really **local** since you get it from elsewhere. But try to produce an example that runs, produces the condition you are asking about, and is short enough we can trace what is happening. – markspace Aug 03 '14 at 22:51
  • I think `ArrayList allSeriesOfNodes = new ArrayList();` is missing a `>` after `MyNode`. – TheCoffeeCup Aug 03 '14 at 23:03
  • @BrettOkken: nodes isn't popped. Correct me if im wrong, but suppose we call this method with A, then B. The B call gets popped. Returning to the A stackframe I see nodes as A, B instead of A. I thought the recursion would remember that going back to where it left off in stackframe A that nodes is A. In terms of a tree it looks like A is the root, B is the left child, and another recursive call with C would have C be the right child – HukeLau_DABA Aug 03 '14 at 23:13
  • 1
    The same `ArrayList` instance is being passed around. See @GaryDrocella answer below for more complete description. – Brett Okken Aug 03 '14 at 23:28

1 Answers1

3

prologue

Edit: After chat with question owner, the answer was still not clear, so I believe it is necessary to articulate the answer better.

It is true that everything in Java is pass-by-value, that is, the values that are on the stack frame can not be changed. The catch is that when passing an object as a parameter to a method, the reference (memory address) is passed as the value to the function. Since the reference is just a value, it is not possible to change the reference itself, but that of course does not mean that you can not change the instance of the object in memory that is being referenced.

This can be some what confusing for beginning programmers and even experienced C/C++ programmers. Classic pointers in C/C++ it is possible to change the reference associated with the pointer, and you can change the value of the object the reference is pointing to. It is therefore, that I declare that Java uses what I call quasi-pointers, quasi meaning ("It is, but it is not") and then pointer, which is the classic C pointer.

Consider the following example of a Non Immutable class (object immutability is important, and could confuse you). Non Immutable basically just means that the state of the object can change.

public static void main(String[] args) {
    NonImmutable ni = new NonImmutable(0);
    mystery1(ni);
    System.out.println(ni.value);
}

public static void mystery1(NonImmutable ni) {
    ni = new NonImmutable(7);
    System.out.println(ni.value);
}

public static class NonImmutable {
    public int value;

    public NonImmutable(int value) {
        this.value =  value;
    }
}

In the above code snippet. you will notice the output is.

7
0

Now let us suppose that when we invoke the application that the main method creates a NonImmutable object on the heap at address 0x123456. When we invoke mystery1, it really looks like this

mystery1(0x123456) //passing the reference to Non Immutable instance as a value!

so inside the function we have ni = 0x123456, but remember it's just a value to the reference, not a classic C pointer. So inside mystery1, we precede to create another Non Immutable object on the heap at another memory address, 0x123abc, which we then set to the value of the parameter ni, so that we have ni = 0x123abc. Now, let's pause and think about this. Since in java everything is pass-by-value, therefore setting ni=0x123abc is no different then setting it to that value if it were a primitive int data type. Hence the value of the object is not changed that was passed in, because the memory address is just a value, which is no different then the value of some int, double, float, boolean, etc.

Now consider the next example...

public static void main(String[] args) {
    NonImmutable ni = new NonImmutable(0);
    mystery2(ni);
    System.out.println(ni.value);
}

public static void mystery2(NonImmutable ni) {
    ni.value = 7;
    System.out.println(ni.value);
}

public static class NonImmutable {
    public int value;

    public NonImmutable(int value) {
        this.value =  value;
    }
}

You will notice the output of the sample above will be.

7
7

We will make the same assumptions as the first example, that is, the initial Non Immutable object is initialize on the heap at memory address 0x123456. Now, when executing mystery2, something quite different happens. When executing the ni.value we are actually executing the below

(0x123456).value = 7 // Set the value of instance variable Non Immutable object at memory address 0x123456 to 7

The above code will indeed change the internal state of the object. Therefore, when we return from mystery2 we have the pointer to the same object that will print a 7.

Now that we understand how Immutable objects work, you may notice there are some unusual cases when Objects are passed, but the above quasi-pointer does not seem to be consistent. Well, it is actually consistent, but there is a different kind of object in Object Oriented Languages that can throw you off to give the illusion of inconsistency, but do not be fooled. There are also objects in Java called Immutable Objects such as the String object specified here (http://docs.oracle.com/javase/7/docs/api/java/lang/String.html).

An Immutable Object simply just means that the internal state of the object can not be changed. Yes, that is correct. Every time you do something as below

String foo = "foo";
String moo = foo + " bar";

moo is not referencing foo, because the String foo is immutable, and therefore the concatenate operation created a whole new String object on the heap when performing foo + " bar". This implies that when passing the String object (or any Immutable object for that matter), as a parameter to a method, you can not alter the state of the Immutable object. Therefore any operations you perform on the Immutable object will actually be creating new Objects on the heap, and will thus be referencing the new heap objects as in mystery1 example above.

Here is a recursive example of Immutable objects being passed.

public static void main(String[] args) {
    foo(4, "");
}

public static void foo(int i, String bar) {
    if(i==0)
        return;

    bar += Integer.toString(i);
    foo(i-1, bar);
    System.out.println(bar);
}

You will notice the output looks something like this

4321
432
43
4

Notice that we don't get something like you might expect 4321 four times. This is because in each recursive call, a new string was created on the heap to represent the new string that was created by the concatenation.

Hopefully that clears up any confusions that the questioner had, and this can be helpful for others.

Another Good Reference http://www.yoda.arachsys.com/java/passing.html.

To the problem at hand

Because when you pass variables through parameters of functions in java, it is a pass by value. In your first call to my method, you are passing in an array list object which is the same pointer through out your entire recursive algorithm. If you want nodes to be a local variable on the stack frame you will have to create a local variable, that is, not specified as a parameter in the method signature

Here is a tutorial that explains how java objects are passed by copied value reference. http://www.javaworld.com/article/2077424/learn-java/does-java-pass-by-reference-or-pass-by-value.html

yea what ever terminology you want to use. The fact is that it's the same pointer.

class MyClass {
ArrayList<ArrayList<MyNode> allSeriesOfNodes = new ArrayList<ArrayList<MyNode>();

public void myMethod(MyNode currNode) {
    List<MyNode> nodes = new ArrayList<MyNode>();  //nodes list as a local variable
    // make sure currNode and nodes aren't null, blah....
    nodes.add(currNode);
    for(MyNode n: otherNodeList) {
        for(listItem in n.getList()) {
            if(...) {
                myMethod(n);
        }
    }

    if(currNode is a leaf and nodes is > 1) {
        allSeriesOfNodes.add(nodes);
    }
}
Gary Drocella
  • 337
  • 1
  • 2
  • 11
  • @HukeLau_DABA why did you down vote me for giving you the correct answer to your question? – Gary Drocella Aug 03 '14 at 23:00
  • java is pass by value, though the values themselves are sometimes references. – HukeLau_DABA Aug 03 '14 at 23:04
  • @HukeLau_DABA Well, won't you be surprised when you find out that your algorithm isn't working, because its the same pointer "reference" to the same array list through your entire algorithm, which is because the objects are passed by reference. – Gary Drocella Aug 03 '14 at 23:08
  • @HukeLau_DABA Here is a great tutorial for you to read. http://www.javaworld.com/article/2077424/learn-java/does-java-pass-by-reference-or-pass-by-value.html – Gary Drocella Aug 03 '14 at 23:12
  • if that's the case, popping stack frames should always have currNode be the leaf node. – HukeLau_DABA Aug 03 '14 at 23:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/58585/discussion-between-hukelau-daba-and-gary-drocella). – HukeLau_DABA Aug 03 '14 at 23:29
  • If you were to do something like currNode.setFoo("bar") then yes. – Gary Drocella Aug 03 '14 at 23:30
  • Thanks for the explanation Gary. If I create a new local list, I can't keep track of the root to leaf path I want. I wanted it to behave like root to leaf, then root to parent of leaf node, etc. – HukeLau_DABA Aug 04 '14 at 16:28