views:

93

answers:

4

Hi,

I was working in a problem and I found that Java references are not working as I expect it to. Ofcourse, I am the culprit :), can someone please me why the following happens. Let me first post the code here.

package misc.matrix;

public class ReferenceTester {
    public static void main(String args[]){
        Boolean[][] input = { 
            {true  ,false ,true ,true ,false },
            {false ,true  ,true ,true ,true },
            {true  ,true  ,true ,true ,true },
            {true  ,true  ,true ,true ,true },
            {true  ,false ,true ,true ,true }
        };

        print(input);

        for(Boolean[] eachRow:input){
            for(Boolean eachVal:eachRow){
                eachVal = Boolean.TRUE;
            }
        }
        print(input);

        /*Expected output  
            true  true  true  true  true  
            true  true  true  true  true  
            true  true  true  true  true  
            true  true  true  true  true  
            true  true  true  true  true
        */
    }

    /**
     * Simply prints the array
     */
    private static void print(Boolean[][] input) {
        for(Boolean[] outArray:input){
            for(Boolean iVal:outArray){
                System.out.print(iVal?iVal+"  ":iVal+" ");
            }
            System.out.println();
        }
    }
}

If you take a look at the above program, all I am trying to do is to change all the values in the Array to true and print it. But its simply prints the input again. Can someone please tell me why is this. Initially I had used the primitive boolean in the program, but since I dont want to create copies, I used the wrapper Boolean Class which is a Java OBJECT as opposed to primitives. (Isnt eachVal a JAVA OBJECT!?!?!?!?) Why is this happening in Java. Why did it not print all the values to be true?

Please advise.

+6  A: 

You cannot modify the source object within a foreach loop. Your loop will have to be a standard for loop like this:

for(int i = 0; i < input.length; i++){ 
    for(int j = 0; j < input[i].length; j++){ 
        input[i][j] = true; 
    } 
}

Edit: To be more precise, eachVal in your loop is a pointer, not a reference; hence setting it to point to a different value does not change the original.

The exact form that the foreach loop uses behind the scenes is given here, if you wish to confirm this independently.

Michael Myers
Yeah.. I tried that.. thats working.. But WHY was my question..
Bragboy
And the reason for that is, I expect, that foreach silently uses a read-only iterator.
Paul Tomblin
@Paul: It does, but that's not the reason. The reason is that taking `.next()` from an iterator into a temporary doesn't mean you can reassign the temporary and expect the original array or Collection to change. (I know you know that, but maybe it also clarifies my edit.)
Michael Myers
Thanks.. Got it.. I thought all wrappers were not immutable.. Now understood.Thanks Myers..
Bragboy
@Bragaadeesh: Yes, if the Boolean class had a setValue(boolean) method, you could use it in the foreach loop to set all the values. But it doesn't, so you have to actually reassign each object (and now there's no reason not to use the primitive, either).
Michael Myers
+2  A: 

Your problem is with the loop to change the value of the array:

    for(Boolean[] eachRow:input){
        for(Boolean eachVal:eachRow){
            eachVal = Boolean.TRUE;
        }
    }

On each iteration, the variable eachVal holds a reference to the content of the cell of the array, just as you expect. But the problem is that, since Boolean is an immutable type---that is, you can't change the value of a Boolean object after it's been created---your assignment doesn't work. In fact, what you're actually doing is changing the object that the eachVal reference points to. That is, since eachVal is an independent variable from input and eachRow, you're simply reassigning this variable without ever touching the contents of the array.

Let me know if you want me to expand upon or clarify any particular point.

RTBarnard
I dont understand the line - you can't change the value of Boolean object after its created. Its not like String. Or is it?I can do something like this. Boolean bool = null; bool = true; bool = false;I can change the boolean value right. Here bool is still a reference isnt it ?
Bragboy
Two points: `Boolean` is an object type, `boolean` is a primitive type. When you create a variable with an object type, that variable holds *nothing* but the address of the object: it's just a *reference* to the object. So when you reassign the value of the variable, you're essentially just giving it the address of a different object. In memory, the `Boolean` object holds its value in a `final` field: its value can only be set on initialization. Does this help or have I misunderstood your question?
RTBarnard
You understood me and what you said cannot convince me any more. :) +1.
Bragboy
+1  A: 

@mmyers had the answer, you asked for more detail but I couldn't fit it in a comment:

So you have an array of references to booleans (it's ALWAYS references) and in your for each loop you create a reference that points to the same boolean as a member of that array.

Each iteration the reference is updated to point to what the next pointer in the array points to.

When you use equals, you are simply re-assigning the reference to point to something else.

If you wanted to update the object inside your array, it would have to be mutable (Boolean is not) and you would have to use a mutator instead of equals.

Bill K
A: 

What you are attempting is analogous to the following:

    int[] arr = { 1, 2, 3 };
    for (int i : arr) {
        i = 0;
    }

This does NOT set all values in the array to 0. Java passes everything by value.

polygenelubricants