Inexplicable Array assignment behavior in Java - java

So here it goes. I've been building a piece of software for a bigger project, and right now, I am simply baffled by the way Java treats my code. I have absolutely no idea as to why Java behaves the way it does right here.
It seems to skip part of my code, and assigns values to a different array than I expected when no according method is called.
I have walked over this for a few hours now with the IntelliJ Debugger, inspecting everything ever so closely, but I have not found a single reason as to why things happen the way they do.
package com.whatareyoudoing.java;
import java.util.Arrays;
/**
* WHAT THE ACTUAL DUCK
*/
public class WTF {
private int[] number;
private int[] oldNumber;
public WTF() {
number = new int[1];
oldNumber = new int[1];
}
public void putNumber(int c) {
number[0] = c;
}
public void putOld() {
if(Arrays.equals(oldNumber, number)) {
System.out.println("Nothing to do!");
return; //How on earth can they literally be the same?!
}
oldNumber = number;
}
public void doWTF() {
putNumber(1);
putOld(); // Works.
putNumber(2); // Expected Result: number = 2; oldNumber = 1 [NOPE] number = 2; oldNumber = 2
putOld(); // [NOPE] Simply Skips with "Nothing to do"
putNumber(3); // Same odd behaviour
putOld(); // Aaaand skips again.
}
}
After calling putNumber the first time, using putNumber again simultaneously puts the value in both variables (oldNumber and Number) instead of only in number[0].
I continued to simplify my code as far as possible so this example is more hands-on. Obviously, the real example where I found this had arrays longer than a single element.
I also tested it with multidimensional arrays, as well as object arrays. No change in the behavior.
I am completely puzzled now and have absolutely no idea how to go on. If you can shed any light on this topic, please do so. I am more than confused.

The following assignment statement:
oldNumber = number;
makes oldNumber and number point to the same underlying array. Perhaps what you want is to make a copy:
System.arraycopy(number, 0, oldNumber, 0, number.length);
See the documentation for System.arraycopy for full details.

This line isn't doing what you think it's doing.
oldNumber = number;
It isn't copying the contents of one array to another. It is making the reference variable oldNumber refer to the same array object as number refers to.
oldNumber ----> [1]
number -------^
So, any change through either variable writes through to the same array, and the change is visible through both references, which refer to the same array.
Then later you call Arrays.equals with references to the same array, so they are "equal".
You want to copy the number with this line instead:
oldNumber[0] = number[0];

When you assign
oldNumber = number
you don't make a copy of the values in the array. oldNumber will point to the exact same array (and future changes in either variable reflect in the other).
You can make a copy with
oldNumber = Arrays.copyOf(number, number.length);

In the putOld function you assigned the reference of the first array to the other. After the first call oldNumber is a pointer to number and if you change a value in one, the other one is affected too.
If you want to copy the values System.arraycopy().

Related

my traverse method wont pass down the value of int but can pass down the value of ArrayList

My god feeling tells me the answer is "Integer and String are immutable", but I want to get confirmation from experts here.
I have a traverse method, which basically will traverse the whole tree starting from the root. Here are two types of usage, first one wont work, always return 0, second one works, the result ArrayList can accumulate in traverse.
Here is the first one:
public int whateverSum(TreeNodeType root, int target) {
int sum = 0;
traverse(root, target, sum);
return sum;
}
Here is the second one:
public List<String> whateverSum(TreeNode root, int target) {
List<String> result = new ArrayList<String>();
traverse(root, target, result);
return result;
}
Please help me the confirm my thought if it is correct.
You got it right. In fact, what happens is that the traverse method passes down the value of an int but not the value of the ArrayList.
Well, what happens is the difference between values and reference. When you are passing down an int to a function, the value is copied. The function accesses the copy of the value. It uses it in whichever way possible, but in the end all of the operations are done to the copy of the value, without the original being modified.
When you are passing an Object to a function, you are passing a reference to it, just like a memory address. It makes sense because when you are passing an ArrayList of millions of items, you would rather not want those millions of items being copied everytime. So, what is being copied is the reference to this Object, which point to the original structure.
That's why, when you pass an int to a function, the original value will never be modified. Whereas when you pass an ArrayList, its content will change.
Now, not all objects behave this way, this is where immutable objects come. You said it String are immutables, which is very handy. So, when you do a modification on a String, it is copied and the original value doesn't change either. Types like Integer or Double are also immutable. It makes lots of sense, because when you're incrementing an integers, you don't want all of the 1 of your program becoming 2's.
Regarding your code, in order to get the first method work, you can do something like:
public int whateverSum(TreeNodeType root, int target) {
int sum = traverse(root, target, 0);
return sum;
}
Where traverse returns the updated sum.

How to create a copy of an instance without having access to private variables

Im having a bit of a problem. Let me show you the code first:
public class Direction {
private CircularList xSpeed, zSpeed;
private int[] dirSquare = {-1, 0, 1, 0};
public Direction(int xSpeed, int zSpeed){
this.xSpeed = new CircularList(dirSquare, xSpeed);
this.zSpeed = new CircularList(dirSquare, zSpeed);
}
public Direction(Point dirs){
this(dirs.x, dirs.y);
}
public void shiftLeft(){
xSpeed.shiftLeft();
zSpeed.shiftRight();
}
public void shiftRight(){
xSpeed.shiftRight();
zSpeed.shiftLeft();
}
public int getXSpeed(){
return this.xSpeed.currentValue();
}
public int getZSpeed(){
return this.zSpeed.currentValue();
}
}
Now lets say i have an instance of Direction:
Direction dir = new Direction(0, 0);
As you can see in the code of Direction, the arguments fed to the constructor, are passed directly to some other class. One cannot be sure if they stay the same because methods shiftRight() and shiftLeft could have been called, which changes thos numbers.
My question is, how do i create a completely new instance of Direction, that is basically copy(not by reference) of dir?
The only way i see it, is to create public methods in both CircularList and Direction that return the variables needed to create a copy of the instance, but this solution seems really dirty since those numbers are not supposed to be touched after beeing fed to the constructor, and therefore they are private.
EDIT1:
CircularList is a class that is a looped around version of ArrayList. You can see that i feed it an array called dirSquare and then a number. The number is an index in that array. So if the number i feed it is for example 3, it will be the last element in dirSquare, which has value of 0. When i use shiftRight and shiftLeft, the index gets incremented/decremented and if i increment pass the size of the last element of the array, it loops around and starts with 0th elemnt. The same thing happens if i go pass 0 on the other side. Thats why its called Circular list. I cant use getXSpeed/getZSpeed because they return the value of the array. I need the index, which is strictly an implementation variable.
A common way to go about this, is to create a copy constructor. An example can be found here
Another solution would be to implement the Cloneable interface, but most developers recommend against it, including Joshua Bloch in Effective Java, calling Java's clone "deeply broken".
Why not add another constructor taking a Direction parameter. Something like:
public Direction(Direction source) {
this(source.xSpeed, source.ZSpeed);
}
I'd add a private/protected constructor without arguments, so you can initialize each member with its own copy (with your constructor it seems you'll have a reference inside your CircularLists, so you need to inizialize them manually inside clone method).
Edited
public Direction() {};
public Direction Clone() {
Direction dir = new Direction();
dir.diSquare=this.dirSquare;
for each elem in xSpeed
dir.xSpeed.Add(elem)
for each elem in zSpeed
dir.zSpeed.Add(elem)
return dir;
}

how can i initialize my array when i cant initialize as null?

i have an array of strings which i want to convert to int, pretty simple and straightforward here is the code :
public static void main(String[] args) {
String myarray[]=readfile("[pathtothefile]");
int mynums[] = new int[myarray.length];
for (int i=0;i<myarray.length;i++){
mynums[i]=Integer.parseInt(myarray[i]);
}
System.out.print(Arrays.toString(mynums));
}
But the Problem here is, if i initialize "mynums" like this: mynums[]=null; i get NullPointerException on the following line:
"mynums[i]=Integer.parseInt(myarray[i]);"
what i have to do to solve it is
int mynums[] = new int[myarray.length];
here someone explained why it happens but i dont know how to initialize now! i mean sometimes i dont know how big my array can get and i just want to initialize it. is it even possible?
In Java everything is a pointer behind the scenes. So when you do mynums[]=null, you are pointing to a null. So what is null[i]? That is where your NPE comes from. Alternatively when you point it to an array, then you are actually accessing the i'th element of the array.
You have to first initialize the array because it allocates memory depending on the array size. When you want to add for example an integer to an array it writes the int into previously allocated memory.
The memory size won't grow bigger as you add more items.( Unless you use Lists or Hashmaps, ... but it's not true for generic arrays)
If you don't know how big your array will be, consider using SparseIntArray. which is like Lists and will grow bigger as you add items.
Briefly, in java an array is an object, thus you need to treat it like an object and initialize it prior to doing anything with it.
Here's an idea. When you're initializing something as null, you're simply declaring that it exists. For example ... if I told you that there is a dog, but I told you nothing about it ... I didn't tell you where it was, how tall it was, how old, male/female, etc ... I told you none of its properties or how to access it, and all I told you was that there IS a dog (whose name is Array, for sake of argument), then that would be all you know. There's a dog whose name is Array and that is it.
Typically, arrays are used when the size is already known and generally the data is meant to be immutable. For data that are meant to be changed, you should use things like ArrayList. These are intended to be changed at will; you can add/remove elements at a whim. For more information about ArrayList, read up on the links posted above.
Now, as for your code:
public static void main(String[] args) {
ArrayList<int> myInts = new ArrayList<int>();
// define a new null arraylist of integers.
// I'm going to assume that readfile() is a way for you get the file
// into myarray. I'm not quite sure why you would need the [], but I'll
// leave it.
String myarray[] = readfile("[pathtothefile]");
for (int i = 0; i < myarray.length; i++) {
//adds the value you've specifed as an integer to the arraylist.
myInts.add(Integer.parseInt(myarray[i]));
}
for (int i = 0; i < myInts.size(); i++) {
//print the integers
System.out.print(Integer.toString(myInts.get(i)));
}
}
What if you don't use an array but an ArrayList? It grows dynamically as you add elements.

Working with an Array of Objects

#edit IT works, thanks for the answers:) I guess my bad was when I thought that
WORLD[i]=global.Values.CHUNKPATTERN();
simply takes the object on the right, clones its value('s), and assigns them to part on the left, while it turns out that it establishes a reference between two. Thanks again:)
I have simple begginer/newbie array problem:
for(int i=0; i<global.Values.WORLDVOLUME(); i++)
// global.Values.WORLDVOLUME() --> const, int. always the same.
{
WORLD[i]=global.Values.CHUNKPATTERN(); //to pre-define as 'zero only' object. Always the same. Const.
WORLD[i].chunknr=i+1;
}
System.out.println(WORLD[4].chunknr);
Of course I want WORLD[0] to have chunknr 1, WORLD[4] to have chunknr of 5 and so on.
Instead WORLD[i].chunknr=i+1; seems to update chunknr of ALL elements(not only WORLD[i]).
So that it looks like WORLD[0].chunknr = WORLD[1].chunknr=global.Values.WORLDVOLUME() here.
Anyone knows how to bypass that? I belive there's a simply solution...
Do I understand the array of objects correctly?
You can Have like(providing you have the class and constructor)
Point POINTARRAY[]= new Point[10];
POINTARRAY[1].x=5
POINTARRAY[1].y=6
POINTARRAY[3].x=17
POINTARRAY[3].y=1
Right?
How to assign that via loop?
Instead WORLD[i].chunknr=i+1; seems to update chunknr of ALL elements.
Are WORLD[0] and WORLD[1] different objects? They are not different if `WORLD[0] == WORLD[1] evaluates to true.
You have:
WORLD[i]=global.Values.CHUNKPATTERN();
Does CHUNKPATTERN create a new object every time it is called?
I bet this method
WORLD[i]=global.Values.CHUNKPATTERN();
always returns the same instance of an object so you have a reference to the same object in every slot of your array.
Subsequently
WORLD[i].chunknr=i+1;
you change the attribute chunknr of the same object in every iteration. You say
...seems to update chunknr of ALL elements
kind of true, because all elements reference the same instance.
You need to find a way to have global.Values.CHUNKPATTERN(); return a new object every time.
This line is your problem:
WORLD[i]=global.Values.CHUNKPATTERN();
This is assigning WORLD[i] a reference to global.Values.CHUNKPATTERN(), meaning that they both point to the same object! And for each iteration of your loop you are just creating more and more references to the same global object.
Sometimes this isn't what you want. In this case you need to copy the value, which can be done in a number of ways, but in most cases you can simple clone it. All Java objects support a clone() method, although sometimes you need to override it to do the correct thing for your class.
All this means is that you should replace the above line with:
WORLD[i]=(YourType)global.Values.CHUNKPATTERN().clone();
where YourType is the actual type of the class, since you omitted that from the code snippet you posted.
Hope that helps!
I guess the following line returns always the same reference:
global.Values.CHUNKPATTERN();
so the different array indices are actually point to the same referece. It's only a guess because you didn't tell us how the above function works.
Here's an example of what different array element could point to the same instace:
public class AClass{
public int val = 0;
}
AClass[] array = new AClass[2];
AClass classInstance = new AClass();
array[0] = classInstance;
array[1] = classInstance;
The code above instatiated a single AClass object (classInstance), but use 2 different array elements to reference the same instance:
System.out.println("array 1 value " + array[1].val ); // both element initialized to 0 so it prints 0
array[0].val = 15; // actually is classInstance.val to be modified, through the reference to it stored inside the first element of the array.
System.out.println("array 1 value " + array[1].val ); // print 15
For what concern the POINT example, you can use for loop this way:
Point POINTARRAY[]= new Point[10];
for(int i = 0 ; i < POINTARRAY.length; ++i)
{
POINTARRAY[1].x=...;
POINTARRAY[1].y=...;
}

Make a copy of a variable and add to Arraylist

I'm working with two-dimensional array-values that should be inserted into a ArrayList. But this is done in a for-loop and the value of the two-dimensional array-value gets changed as the loop runs since it is just used as an temp-variable (which makes all of the variables stored in the ArrayList gets changed as this variable changes).
So if I try to print out the content of the ArrayList when the loop is done all the values are the same.
for(int i = 0; i <= Counter; i++)
{
if(Xhavetomove >= i)
arrayvalue[0][0] = this.Xspeed;
else
arrayvalue[0][0] = 0;
if(Yhavetomove >= i)
arrayvalue[0][1] = this.Xspeed;
else
arrayvalue[0][1] = 1;
System.out.println(arrayvalue[0][1]);
Object.movement.add(arrayvalue);
}
Are there anyway I can make it store the value itself?
For example: The first time the loop runs the value is "5,5" but if I print out the ArrayList when the loop is done all the values has turned into "5,1".
The problem is the way Array is added to the Object here. You are not adding the Array to the Object. What is happening is you are adding the address to the location in memory where the Array resides. So every time you add the Array to the Object, you are adding the same address every time. So every Array in the Object is actually the same Array over and over since they all point to a single location in memory. So when you change the Array, it will appear to change all of them inside the Object.
The best thing to do is either create a new Array every time through the loop, essentially creating a new location in memory for the Array to reside, or clone() the Array which will create a new reference.
Example:
String[] houseOfStark = {"Eddard", "Catelyn",
"Robb", "Sansa", "Arya", "Bran", "Rickon"}; // Sorry Jon
String[] copyOfStark = houseOfStark;
String[] cloneOfStark = houseOfStark.clone();
houseOfStark[1] = "Lady Catelyn";
System.out.println(houseOfStark[1]);
System.out.println(copyOfStark[1]);
System.out.println(cloneOfStark[1]);
Will produce:
Lady Catelyn
Lady Catelyn
Catelyn
Good blog post explaining the difference
At the end each add needs to create an own object.
To use clone is one way.
Or to add always the values in pairs, in an other.
A totally different way is to use serialization. This make sense when you do not want to calculate this values but to log it. In this case you need an outputStream
What is best is defined by what you want to do with Object.movement
You need to use array's clone() method to make its copy:
//for example
int[][] copy = (int[][])arraySource.clone();

Categories