1

Why does changing directly a value in an parameter array becomes pass-by-reference but if I try to just pass the reference to the array it isn't pass-by-reference?

What is the most effective way to force it to be "pass-by-reference" in array = arrayII;, if there is one?


public static void main(String[] args) {
    // TODO code application logic here
    String [] array = {"Default1","Default2"};
    System.out.println("Start: " +" "+ array[0] + array[1]);
    test(array,false);
    System.out.println("false: " +" "+ array[0] + array[1]);
    test(array,true);
    System.out.println("true:  " +" "+ array[0] + array[1]);        
}

static void test(String[] array, boolean change){
    String [] arrayII = {"Changed1","Changed2"};    
    if (change){
        array[0] = "Changed1";
        array[1] = "Changed2";
    }
    else {
        array = arrayII;
    }
}

run:

Start:  Default1Default2
false:  Default1Default2
true:   Changed1Changed2
BUILD SUCCESSFUL (total time: 0 seconds)
user207421
  • 305,947
  • 44
  • 307
  • 483
Hugo
  • 19
  • 2
  • `array[0] = arrayII[0]; array[1] = arrayII[1];` ? – nickb Feb 10 '16 at 00:51
  • 1
    Because Java is [pass-by-value](http://stackoverflow.com/q/40480/3425536). – Emil Laine Feb 10 '16 at 00:52
  • It IS being passed-by-reference both times! It's just that you aren't able to see that from your output because you aren't changing any values when `change` is false, and you are making a bad assumption that changing the array _pointer_ within the method would change the underlying values of the array outside the method. – 4castle Feb 10 '16 at 06:06
  • Better phrasing would be: it is being passed-by-memory-address – 4castle Feb 10 '16 at 06:13
  • Please select an answer if your question has been answered, or give some feedback – 4castle Feb 22 '16 at 16:15

6 Answers6

1

Because array = arrayII; only updates the reference locally to the method. The method cannot update the value of the caller's reference to array (however, you could return arrayII and assign that in the caller).

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • I really need a way to force it, because in my real method I'm already returning a boolean. This was just a small test to try to narrow down why my class wasn't working. – Hugo Feb 10 '16 at 00:59
  • 1
    You can copy the values into the *destination* `array` with one line like, `System.arraycopy(arrayII, 0, array, 0, array.length);` but you still cannot change the caller's reference to `array` (which means, for example, that you cannot resize `array` in your method). – Elliott Frisch Feb 10 '16 at 01:09
  • Thanks, that appears to work. I actually tested that one before but I had: "array = arrayII; System.arraycopy(arrayII, 0, array, 0, arrayII.length);" The "array = arrayII" before arraycopy was blocking the update of the reference??? . If I remove the "array = arrayII;" It works. Java is very strange. – Hugo Feb 10 '16 at 01:17
  • Hugo pls check out the first example in my answer, it will explain everything. Your example is just ambigous because you use the same value in 2 cases. – HopefullyHelpful Feb 10 '16 at 05:42
1

Here, this example shows a different angle, and I think it will clear up the confusion. Let me know. Don't get scared by the code, just look at the output.

Code:

import java.util.Arrays;
public class Test {
public static void main(String[] args) {
    String[] array = {"Default1" ," Default2"};

    System.out.println("array memory address outside before: " + array);
    System.out.println();
    test(array);
    System.out.println();
    System.out.println("array memory address outside after: " + array); 
    System.out.println("array values: " + Arrays.toString(array)+ "\n//The original reference still points to the same memory and value have not changed");
}

private static void test(String[] array) {
    String[] arrayII = { "Changed1", "Changed2" };

    System.out.println();
    System.out.println("array values: " + Arrays.toString(array));
    System.out.println("arrayII values: " + Arrays.toString(arrayII));
    System.out.println();


    System.out.println("array memory address inside before:\t" + array);
    System.out.println("arrayII memory address inside before:\t" + arrayII);

    array = arrayII; // <<<<<<< The statement in question <<<<<<<<<

    System.out.println();
    System.out.println("array values: " + Arrays.toString(array));
    System.out.println("arrayII values: " + Arrays.toString(arrayII));
    System.out.println();

    System.out.println("array memory address inside after: " + array);
    System.out.println("arrayII memory address inside after: " + arrayII);
}
}

Output:

array memory address outside before:    [Ljava.lang.String;@3fae653c <<this

array values before: [Default1,  Default2]
arrayII values before: [Changed1, Changed2]

array memory address inside before:     [Ljava.lang.String;@3fae653c
arrayII memory address inside before:   [Ljava.lang.String;@24abbfad

array memory address inside after:      [Ljava.lang.String;@24abbfad
arrayII memory address inside after:    [Ljava.lang.String;@24abbfad

array values after: [Changed1, Changed2]
arrayII values after: [Changed1, Changed2]


array memory address outside after:     [Ljava.lang.String;@3fae653c <<this
array values after method: [Default1,  Default2]

The original reference still points to the same memory and values have not changed.

Notice how the memory addresses of array at the beginning and end are exactly the same? It's because the method is getting the pass-by-value of the memory address, not the values, which means that after the method, the pass-by-value can't communicate with what just happened to the version of array inside the method.

HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
4castle
  • 32,613
  • 11
  • 69
  • 106
  • Please ask if any of those outputs don't make sense to you. Because I can explain them if needed. – 4castle Feb 10 '16 at 07:19
0

Your arrays are equal in content but array == array2 is false, because they point to different locations.

Java is pass-by-reference-value or pass-by-sharing. That means the method/function has a copy of your reference-value, but can never change the location your reference is pointing to, because it doesn't have the original reference, only a copy of the reference.

public static void main(String[] args) {
    // TODO code application logic here
    String [] array = {"array[0]","array[0]"};
    System.out.println("Start: " +" "+ array[0] + array[1]);
    test(array,false);
    System.out.println("false: " +" "+ array[0] + array[1]);
    test(array,true);
    System.out.println("true:  " +" "+ array[0] + array[1]);        
}

static void test(String[] array, boolean change){
    String [] arrayII = {"array2[0]","array2[1]"};    
    if (change){
        array[0] = "anotherValueForArray[0]";
        array[1] = "anotherValueForArray[1]";
    }
    else {
        array = arrayII;
    }
}

I modified your example to illustrate it better.

HopefullyHelpful
  • 1,652
  • 3
  • 21
  • 37
  • This example isn't any improvement over what was in the question. It has the same looking output, and from his eyes, that output would still be saying that when `change = false` it isn't getting passed-by-reference because the `array2[0] array2[1]` never shows up in the output. Give an example that exemplifies how `array == array2` is false and that's why it appears that it isn't doing pass-by-reference. – 4castle Feb 10 '16 at 06:19
  • Yes, but that's not having any effect on the output or his understanding. He thinks that if he doesn't see the values for `arrayII` in his output, that it means it didn't pass-by-reference. You are only furthering that assumption by producing code with the same output & therefore the same false conclusion. – 4castle Feb 10 '16 at 06:31
  • Why else would he have titled this question the way he did? He thinks the mutation of values when `change = true` is **causing** the array to become pass-by-reference. – 4castle Feb 10 '16 at 06:36
  • I ran your code. The values for `arrayII` does not show up in the output – 4castle Feb 10 '16 at 06:37
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/103049/discussion-between-4castle-and-hopefullyhelpful). – 4castle Feb 10 '16 at 06:39
  • Change you example so that it outputs the memory addresses of `array` before the method, `array` within the method, `arrayII` within the method, and `array` after the method – 4castle Feb 10 '16 at 06:48
  • Look at my new answer I just posted. The `Object.toString()` prints the memory address. And lucky me, arrays don't overwrite that `toString()`, so yes, you can output memory addresses. – 4castle Feb 10 '16 at 07:18
  • Remove your comments pls, your answer shows what you wanted to show. The discussion has become obsolete. – HopefullyHelpful Feb 10 '16 at 07:39
-1

This has to do with how variables are stored in the computer in Java. "Shallow" variables (primitive data types like int or char) cannot be changed through a pass by reference, they are stored in the Stack. However, "deep" variables (like objects or arrays) are reference types which are also stored in the Stack but point to primitive data types (or even other objects) being stored in the Heap. If you were to pass an object or array as a parameter, it would make a local copy of the memory address for that object/array, but it would not be making a copy of the underlying values, it would receive the same variable access to the underlying values as the calling scope. Therefore, passing by reference via an object or array is the only way (in Java at least) to "cheat the system" and give the method extra control that otherwise shouldn't be possible from that scope.

To answer your question, the best way in Java to do a pass-by-reference is to create a wrapping object or array for your variable so that all of the underlying variables will be directly mutable in the method.

Example: Say you want to reassign a value to variable in the method OtherType.methodCall()... (variable is whatever datatype you want, even an array)

ValueType variable = 1;
ValueType[] wrapperForVariable = { variable }; //Wrap it up!
OtherType.methodCall(wrapperForVariable);
variable = wrapperForVariable[0];

variable == 2 // true!

...

public class OtherType {
    public static void methodCall(ValueType[] localVar) {
        // Stuff happens based on localVar[0]
        ...
        localVar[0] = 2;
    }
}

Result: variable has now been changed so that it has a different value, even though it was put into a method marked void!!

P.S. Strings are different because, while they are objects, under the hood they are actually immutable. Any change to a String actually makes a "deep" copy, meaning Strings would need wrappers too.

4castle
  • 32,613
  • 11
  • 69
  • 106
  • In a language that actually supports pass-by-reference (which Java does *not*), you can *absolutely* update the value of primitive types through the reference. That's what *makes* it pass-by-reference. Java is strictly pass-by-value. It just also happens to use reference semantics. – Ian McLaird Feb 10 '16 at 01:04
  • @Ian Sorry, I should have specified that this is only applicable to Java. Yes, I'm aware of how other languages have pointer and reference types. – 4castle Feb 10 '16 at 01:17
  • I have made several changes now to my answer which you may find helpful Mr. Hugo – 4castle Feb 10 '16 at 04:21
-1

Java by default passes primitive data types like strings and ints by value and NOT by reference.

Your best bet for this code would be to return a value from the function test.

In a more elaborate program you could also make array a variable in a class.

6godmike
  • 19
  • 4
  • Java passes **everything** by value. But the value of non-primtives (or reference types, or descendants of `java.lang.Object`) **is** a reference. – Elliott Frisch Feb 10 '16 at 01:11
  • In addition to the fact that Java *never* passes by reference as Elliott Frisch mentioned String is *not* a primitive data type. See [this link](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) for more information – Bunti Feb 10 '16 at 01:44
-1

It looks like the confusion here is on the distinction between pass-by-value and pass-by-reference. Java only supports pass-by-value. When you call test(array, someValue), the value of of the first argument is a reference to the array. So inside of test, when you overwrite the local value of array, you are overwriting a reference to the array you passed in. The data in the array you passed in, though, is untouched.

The two main solutions here are a) update elements in the array that was passed in, as you did in your example, or b) return arrayII, but update the code in your main method to say array = test(array);.

The tl;dr is that java only uses pass-by-value.

tyler
  • 91
  • 5