Final array is being changed

I’m making a platforming game that uses arrays to store the 32x32 tile playfield. There are three arrays that are used in this process. When I construct a Level class object, a specific array is put in that constructor.

final int[][] layout00 = { ... };
...
levels[0] = new Level(layout00);

Notice how the array I used was Final. It will become important later.
Within the constructor itself, the array is copied to a new array, layout[][], which is local to each instance of the object.

Level(int tempLayout[][]) {
    arrayCopy(tempLayout, layout);
}

The third array in the sequence is stage[][]. On a level loading in, it copies it’s layout[][] to the stage[][] and layout[][] is never mentioned anywhere else in the code.

void load() {
    arrayCopy(layout, stage);
}
...
void resetLevel() {
  levels[currentLevel].load();
}

The idea is that the player can change tiles in the level, but not actually change anything in the level’s layout[][]. This is so when the player has made a mistake, they can press a key to reset the level to it’s initial state. I do this by reloading the level with load(), again.

For some odd reason, the level doesn’t change at all. All the changes the player made to the stage[][] are still there. After some debugging, I found that somehow, the levels layout[][] was being edited as stage[][] was being edited. This should be impossible because, after some further searching, there is no code outside of the Level class that references the level’s layout[][].

Some more debugging lead me to find the final array, layout00[][], was being edited as well. How is this possible? There is no code in my project that even references layout00[][]. And I’m not even getting any error messages that it is unable to be edited!

Java’s keyword final, just like JS’ keyword const, when used on a variable (be it a field, a parameter or a local variable), permanently seals the value stored in that variable, disallowing any further re-assignment to it. :face_with_monocle:

By value, it’s not just numerical 1s, but also references (a.K.a. pointers or memory addresses). :open_mouth:

In the statement above, the memory address of the array object created on the right side is permanently assigned to the variable layout00. :sunglasses:

We can’t re-assign layout00 to some other array any longer. Not even a null. :zipper_mouth_face:

However, the contents of that array, which variable layout00 points to, are still free to modify. :scream:

Okay, but layout00 is never referenced in my code. So what could be changing it?

Some of my clone() examples I did a long time ago at the link below may help ya out: :smile_cat:

final int[][] stuff = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };

final int[] myArray = stuff[2]; // it's an alias for stuff[2].
myArray[0] = 10; // thus it alters stuff[2] too!
println(stuff[2][0]); // prints out 10. stuff[2] is also affected!

final int[] myClone = stuff[2].clone(); // it's a clone for stuff[2].
myClone[0] = 20; // thus it's independent from stuff[2]!
println(stuff[2][0]); // still prints out previous 10. stuff[2] isn't affected!

exit();
final int[][] stuff = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
for (final int[] thing : stuff)  println(str(thing));
println();

final int[][] myClone = stuff.clone(); // Cloning the outer dimension of the 2D array.
for (final int[] clone : myClone)  println(str(clone));

myClone[0][0] = 100;
print('\n', stuff[0][0]); // Prints out 100. It's still not a real clone!

// In order to fix that, we gotta clone() each of its inner arrays too:
for (int i = 0; i != myClone.length; myClone[i] = stuff[i++].clone());

myClone[0][0] = 200;
println('\n', stuff[0][0]); // Still prints out previous 100 and not 200.
// It's a full clone now and not a mere reference alias!

exit();
1 Like

Okay, your second block of code worked for me, but I don’t think I understand why.

for (int i = 0; i != clone.length; clone[i] = stuff[i++].original());

Do you have any insight as to why processing links two arrays together if you copy the contents from one to the other?

Also, this didn’t happen in my previous game I made in processing. I used the same arrayCopy() and it did not cause any issues.

The 1st example explains at its comments why the 2nd 1 works. :wink:

Processing got nothing to do w/ that, but the Java language itself! :coffee:

As you know by now, the 1st example already got those basic insights. :roll_eyes:

Basically, we can have more than 1 variable (and array slots) pointing at the same object.
They simply become alias for the same object. :flushed:

That is, if we mutate an object via 1 of its alias, its other aliases see those changes as well, b/c they all store the same memory address of that object. :confused:

So when we clone an object, we can’t use the assignment = operator (or just an arrayCopy()) alone. :drooling_face:

We need to check whether that object offers a clone() method, or some other analogous 1. :cowboy_hat_face:

This way we get another object (and thus a diff. memory address value), but w/ the same content as the original 1. :star_struck:

2 Likes

Thank you! Very helpful. I have everything going for me now, but the only question I have is that I had made a game in processing 3 previously that used the arrayCopy() thing, but no problems occurred. Although, that might be because of how I referenced the arrays or something like that. Thank’s, GoToLoop, you’re always quick to answer on the forums!

Maybe back then it was a 1D array. This time you’re using a 2D 1! :speak_no_evil:

Each inner array is a memory address stored by the outer array that needs to use clone(). :roll_eyes: