Do Arrays in Java Violate “Call by Value”?

jdeisenberg

J David Eisenberg

Posted on March 23, 2022

Do Arrays in Java Violate “Call by Value”?

“Any headline that ends in a question mark can be answered by the word ‘no’.”
-- Betteridge’s Law of Headlines

When you first learned about methods in Java, you might have read something like “arguments are always passed to parameters by value.” This means that a parameter is always a copy of the argument. You can change the value of the parameter all you want, and the original argument will remain unaffected. In this code:

public class ByValue1 {
    public static void printCube(int n) {
        n = n * n * n;
        System.out.println("The cube is " + n);
    }

    public static void main(String[] args) {
        int n = 12;
        System.out.println("Before call, n is " + n);
        printCube(n);
        System.out.println("After call, n is " + n);
    }
}
Enter fullscreen mode Exit fullscreen mode

The output shows that n in main() remains unchanged:

Before call, n is 12
The cube is 1728
After call, n is 12
Enter fullscreen mode Exit fullscreen mode

This is a good thing; imagine calling printCube(3); you certainly wouldn’t want Java to change the literal 3 to 27!

Further along in your adventures with Java, you encountered arrays, and may have seen a program like this:

import java.util.Arrays;

public class ByValue2 {
    public static void cubeArray(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * arr[i] * arr[i];
        }
    }

    public static void main(String[] args) {
        int [] data = {10, 2, 4};
        System.out.println("Before call, data is " +
           Arrays.toString(data));
        cubeArray(data);
        System.out.println("After call, data is " +
            Arrays.toString(data));
    }
}
Enter fullscreen mode Exit fullscreen mode

When you run this program, you get this output:

Before call, data is [10, 2, 4]
After call, data is [1000, 8, 64]
Enter fullscreen mode Exit fullscreen mode

What’s going on here? The array contents have changed! Is this an exception to “always call by value“? No, it isn’t. The key point here is that arrays are references. Here’s what a memory diagram looks like when we enter the cubeArray method:

Two references to same area of memory

When doing the multplication arr[i] * arr[i] * arr[i], Java is changing element i in the memory that arr refers to; it is not changing arr itself. We can prove this by printing out the reference, which will show the memory address of the arrays:

import java.util.Arrays;

public class ByValue3 {
    public static void cubeArray(int[] arr) {
        System.out.println("At start of cubeArray, arr is "
            + arr);
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * arr[i] * arr[i];
        }
        System.out.println("At end of cubeArray, arr is "
            + arr);
    }

    public static void main(String[] args) {
        int [] data = {10, 2, 4};
        System.out.println("Before call, data is " +
            Arrays.toString(data) + " reference " + data);
        cubeArray(data);
        System.out.println("After call, data is " +
            Arrays.toString(data) + " reference " + data);
    }
}
Enter fullscreen mode Exit fullscreen mode

When you print an array with System.out.println, you will get something like [I@764c12b6, which means you have an array ([) of integers (I) at memory location 764c12b6 (@764c12b6). Running the program shows that the references have never changed, but the referred-to array has:

Before call, data is [10, 2, 4] reference [I@764c12b6
At start of cubeArray, arr is [I@764c12b6
At end of cubeArray, arr is [I@764c12b6
After call, data is [1000, 8, 64] reference [I@764c12b6
Enter fullscreen mode Exit fullscreen mode

So, in fact, this program doesn’t settle the question of call by value one way or the other, since nothing here has changed the reference arr itself. Let’s put that to the test by doing something that will change arr—we’ll set arr to refer to a brand new array:

public class ByValue4 {
    public static void cubeArray(int[] arr) {
        System.out.println("At start of cubeArray, arr is "
            + arr);
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * arr[i] * arr[i];
        }
        // make arr refer to a different area of memory
        arr = new int[4]; 
        System.out.println("At end of cubeArray, arr is "
            + arr);
    }

    public static void main(String[] args) {
        int [] data = {10, 2, 4};
        System.out.println("Before call, data is " +
            Arrays.toString(data) + " reference " + data);
        cubeArray(data);
        System.out.println("After call, data is " +
            Arrays.toString(data) + " reference " + data);
    }
}
Enter fullscreen mode Exit fullscreen mode

Here’s the memory diagram just before we leave cubeArray():

parameter refers to different area of memory

If call by value really works everywhere, the reference to data in main() should not change. Let’s see the output:

Before call, data is [10, 2, 4] reference [I@764c12b6
At start of cubeArray, arr is [I@764c12b6
At end of cubeArray, arr is [I@4e0e2f2a
After call, data is [1000, 8, 64] reference [I@764c12b6
Enter fullscreen mode Exit fullscreen mode

The third line of the output shows that we changed the arr reference (the parameter) in the cubeArray() method, but the fourth line shows that the original data reference in main() was not affected. The reference is, indeed, passed by value.

Thus, the answer to the article headline: Do Arrays in Java Violate “Call by Value”? The answer is “no.“ Java is always call by value.

💖 💪 🙅 🚩
jdeisenberg
J David Eisenberg

Posted on March 23, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related