Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strings Created inside a Java For Each Loop eligible for GC

Tags:

java

arrays

Coming from different languages, this seems to confuse me.

String [] names = new String[]{"A","B","C"};
for (String n : names){
    n = new String(n+"hello");
}
System.out.println(Arrays.toString(names)); // [A, B, C]

names still has original values [A,B,C], I believe this is due to immutability, but does this mean I have created three garbage collectible Strings when I iterated through the array?

Yet this code does modify array?

Car [] cars = { new Car("red"), new Car("green") };
    for(int i=0; i<cars.length; i++){
        Car c = cars[i];
        c.color ="black";
    }

    for(int i=0; i<cars.length; i++){
        Car c = cars[i];
        System.out.println(c.color);    //prints black, black
    }
like image 539
user1529412 Avatar asked Jan 09 '23 00:01

user1529412


2 Answers

This code

for (String n : names){
    n = new String(n+"hello");
}

is same as

for (int i = 0; i<names.length; i++){
    String n = names[i];
    n = new String(n+"hello");
}

So as you see assigning n new value can't affect array. And yes, at end of each iteration String created via new String(n+"hello"); will be eligible for garbage collection.

But since n by default holds same object as names[i] you can use it to change state of held object. For instance if your array would be array of Persons, via n you would be able to change property like age of stored Person

n.setAge(42);

You can also assign new value to n,

n = new Person("Jack", 40);

but this doesn't mean that you are assigning new value to names[i] because n and names[i] are different variables.

Maybe this will help you a little

when n = names[i] situation looks like this:

names[i] ----+       +--------+
             |       | Person |
             +------>+--------+
             |       | name   | //Adam 
       n ----+       | age    | //30
                     +--------+

so both variables (references) hold same instance, which allows you to manipulate it from both variables (if you change name or age via n you will be also to see this change also via names[i] because they hold same object)

But if you do

n = new Person("Jack", 42);

you are assigning new object only to n, not to names[i], so situation will be

                     +--------+
                     | Person |
names[i] ----------->+--------+
                     | name   | //Adam 
                     | age    | //30
                     +--------+

                     +--------+
                     | Person |
       n ----------->+--------+
                     | name   | //Jack
                     | age    | //42
                     +--------+

which means that array (names[i]) doesn't change.

In short via n you can't put new elements to array, but you can change state of existing elements.

like image 161
Pshemo Avatar answered Jan 16 '23 22:01

Pshemo


In short, yes you did create 3 GC eligible String objects inside the loop, because while you created the 3 String's, they are immediately out of context when the loop exits, because the variable n only exists within the for each loop.

As a side note, you can't change the values within the names array while you are iterating over it using a for-each construct.

String [] names = new String[]{"A","B","C"};
for(String n : names){
    //There is no way you can modify the array within the loop,
    //You can only read out of it.
    //Because n only has meaning inside the loop, any changes to it,
    //only have meaning inside the loop.

     n = "The Spanish Inquisition";

    //No one expects the above assignment to change the value of
    //names array value that n was originally, because n is just a
    //reference of the value held within the array.
}
return names;

If you do try to modify names within the loop it should throw a ConcurrentModificationException. The String's that were created within the loop, are out of context as soon as you exit the loop, and so are eligible for GC.

Updated Code Explanation:

Your updated code works because you are accessing each object within the array individually, changing each object which then implicitly updates the value in the array. Using a normal for loop to do this is fine, because you are using something external to the object you are iterating over to control the iteration (in this case a variable called i).

You are essentially doing this:

for(int i = 0; i < cars.length; i++){
    cars[i].color = "black";
}

Because you don't use Car c = new Car(cars[i]); you get the actual Car object instance that is contained inside the array, not a copy of the Car object contained inside the array which is what you seem to be expecting to get.

You won't get a different object reference back unless you specifically invoke a constructor. For example you could do the following:

for(int i = 0; i < cars.length; i++){
    Car c = new Car();
    c.setColor(cars[i].getColor());
}

which would not modify the value of the car object inside the cars array.

like image 27
JamesENL Avatar answered Jan 16 '23 20:01

JamesENL