0

I've come across a problem which simplifies down to something like this:

public static void main(String[] args) {
    ArrayList<String> firstList = new ArrayList<>();
    firstList.add("Hello");
    firstList.add("World");
    firstList.add("This");
    firstList.add("Is");
    firstList.add("A");
    firstList.add("Test");

    System.out.println("Size of firstList: " + firstList.size());

    ArrayList<String> tempList = firstList;
    System.out.println("Size of tempList: " + tempList.size());

    firstList.clear();
    System.out.println("Size of firstList: " + firstList.size()); // should be 0
    System.out.println("Size of tempList: " + tempList.size());
}

And the output is:

Size of firstList: 6
Size of tempList: 6
Size of firstList: 0
Size of tempList: 0

I would expect the size of tempList the second time round to be 6 instead of 0.

There have already been some questions relating to this effect such as this one and another one.

From the answers, I've found that this is because tempList refers to the same reference as firstList so when firstList changes so does tempList (correct me if I'm wrong here).

So a better solution to this would be something like:

ArrayList<String> tempList = new ArrayList<String>(firstList);

If the information about references mentioned above is true, then why does this code:

public static void main(String[] args) {
    int firstValue = 5;

    System.out.println("firstValue: " + firstValue);

    int tempValue = firstValue;
    System.out.println("tempValue: " + firstValue);

    firstValue = 3;
    System.out.println("firstValue: " + firstValue);
    System.out.println("tempValue: " + tempValue);
}

give this output:

firstValue: 5
tempValue: 5
firstValue: 3
tempValue: 5

?

Should tempValue not also be 3 the second time it is printed?

I feel like I'm misunderstanding how references work, so could someone explain why the temporary list and original list in the first example are affected together, whereas the temporary integer and original integer give different results as in the second example?

Farbod Salamat-Zadeh
  • 19,687
  • 20
  • 75
  • 125

4 Answers4

2

Java has two variable types: primitives and references. When you assign one variable to another, as in

ArrayList<String> tempList = firstList;

you are assigning the reference stored in firstList to be the value of the variable tempList. It's important to note that you are not creating a new ArrayList object with this assignment; you are simply copying a reference value.

When you do assignment with primitives:

int tempValue = firstValue;

you are doing the same thing, but with a primitive value.

In general, think of it this way. Variables do not share memory locations; each variable has its own value. When the value is a reference, however, two reference variables may contain the same reference (just as two primitive variables may contain the same primitive value, such as 5). For reference types, if you use one variable to change the contents of the object being referenced, then you will see the change in that object when accessed through the second variable. In the computer science literature, this behavior is called aliasing. For primitives, this aliasing doesn't happen.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
1

For the ArrayList example, both variables point to the same object elsewhere in heap memory, thus changing the contents of one reference affects the other.

As for the integer example, the current behaviour is expected as they're primitive types and not reference types. When it comes to primitive types, the variables store the value itself rather than a reference to an object in memory.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
1

int tempValue is a primitive type which means that it stores directly by value. ArrayList tempListis not a primitive type and thus stores by reference.

What you see happening with int would also happen with all other Java static-type primitive variables. Meanwhile, Java non-primitive variables will change upon mutation whenever the instance to which they refer changes in value.

(This leads to a follow-up question: what happens to tempList when you use firstList = new ArrayList() instead of firstList.clear()? Is its value the same between re-assignment and mutation and why?)

0

Because primitive values do not have references, and the wrapper types are immutable (so Integer behaves the same way). Arrays, as a counter example, are reference types (even arrays of primitives). So,

int[] arr = { 1 };
int[] arrCopy = Arrays.copyOf(arr, arr.length); // <-- deep copy
int[] b = arr; // <-- shallow copy
b[0] = 2; // <-- same as arr[0] = 2
System.out.printf("arr = %s, arrCopy = %s, b = %s%n", //
        Arrays.toString(arr), Arrays.toString(arrCopy), //
        Arrays.toString(b));

Outputs

arr = [2], arrCopy = [1], b = [2]

See also, What is the difference between a deep copy and a shallow copy?

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249