Undesrstanding clone method for arrays - java

I've learnt that for arrays the clone method is well-behaved and we can use it. But I thought that the type of the elemetns the arrays hold should have implemented Cloneable interface. Let me provide some example:
public class test {
public static void main(String[] args){
Test[] arr_t = new Test[1];
arr_t[0] = new Test(10);
Test[] an_arr = arr_t.clone();
an_arr[0]= new Test(5);
System.out.println(an_arr[0].test); //5
System.out.println(arr_t[0].test); //10
}
public static class Test{
public int test;
public Test(int test) {
this.test = test;
}
}
}
DEMO
I thought that 5 should have been printed twice. The reason for that is by clonning arrays we're creating new arrays containing references to the objects the first array holded (Because the type of the elemnts does not implement Cloneable). Couldn't you straighten the things out?
It's not clear if it's neccesary the array element's type implements Cloneable.

It's a simple concept, but seems hard to explain. When you clone the array, you will have two district arrays in memory, but with the same values in their indexes.
In your second array an_arr doing an_array[0] = new Test(5) you will put another reference in the slot 0.
But the index 0 of the clone is in a different place from the cloned array.
Your example is valid if you do
Test[] arr_t = new Test[1];
arr_t[0] = new Test(10);
Test[] an_arr = arr_t.clone();
an_arr[0].test = 5; // look here, I changed the object
System.out.println(an_arr[0].test); //5
System.out.println(arr_t[0].test); //5
Now both will print the same value because they hold the same reference to the same object, but the place where this reference is stored, is different.

.clone() makes what's called a "shallow copy". That means it copies as little as possible when running the method; instead of copying the contents of the array, it just creates references to the contents of the original array.
If you change the references by calling:
new Test(5)
Then you've overwritten that reference, and get new data.
The alternative is a deep copy of the array. Those can be expensive to do on bigger objects, so they're not the default.

An array's clone() does the same thing regardless of type of array: it creates a new array of the same type and size and then it assigns (=) each element of array to each element of the new array. That's it.
So if you have a 3-element array pointed to by arr, and arr.clone() returns newArr, then after the creation of the new array it simply does this:
newArr[0] = arr[0];
newArr[1] = arr[1];
newArr[2] = arr[2];
That's it. The type of array is not relevant. There is no "cloning" or anything involved in the elements. It doesn't know (or care) if the element has some kind of method to clone (in fact, there is no general API for cloning in Java, anyway, so this would be impossible).
(And by the way, even if it somehow put cloned objects into the new array, the result in your example would still be the same, because you are assigning a new value into the array, so it doesn't matter what was there before.)
So your question is basically why:
Test[] arr_t = new Test[1];
arr_t[0] = new Test(10);
Test[] an_arr = new Test[1];
an_arr[0] = arr_t[0];
an_arr[0] = new Test(5);
System.out.println(an_arr[0].test); //5
System.out.println(arr_t[0].test); //10
and you can figure it out from there.

The "problem" here is that arrays hold references to object, not objects. Further, this has nothing to do with cloning.
When you execute:
an_arr[0] = new Test(5);
You are assigning the new object's reference to the first element.
Also, each array has its own references to Test objects. When you clone the array, only the references are copied (not the objects to which they refer).

Related

Shallow copying with primitives

From what I understand about Java (which is arguably pretty little =P), Java treats primitive values differently than objects. One example I can think of is that primitives are passed by value, while objects are passed via the value of their references.
Extending this, I have implemented code that creates an array of int, and shallow copied it over to a new array of int:
public class Test {
public static void main(String[] args){
int[] array = {2,3,4,5};
int[] copy = array;
copy[1] = 0;
for (int i : copy){
System.out.print(i);}
System.out.println();
for (int i : array){
System.out.print(i);}
}
}
However, the output is:
2045
2045
So, what puzzles me is why do both arrays change? I thought when we made a copy of the original array, we only copied over the values of the elements (since we are dealing with primitives)?
You didn't copy the array at all. What you did was make the references array and copy point to the same array in memory. Thus, when you change the array via one reference, the other reference will point to the same change.
Arrays are objects. When you set
int[] copy = array;
both copy and array refer to the same object. Your update
copy[1] = 0;
updates "both".
When you made the assignment copy[1] = 0, you told Java to take the array which copy refers to, go to position 1, and assign the value 0. Since you also have the following statement
int[] copy = array;
then copy and array refer to the same array in memory. You never really made a shallow copy, you only assigned another variable to point to the same array.
If you want to make a copy of the array, then you could try:
int[] copy = Arrays.copyOf(array, array.length);

Reference new object through void method

I'm working on an assignment that deals with array manipulations for Java. Right now I have to delete elements inside the middle of an array. Now I know that arrays, once they're created cannot be changed in length. So I decided to make a new object, and have the former reference point to my new array.
public class A{
public static void main(String[] args){
B test = new B(val);
test.cut(2,4);
test.display();
}
class B{
private Obj[] array;
B(Obj val){
construct something
}
public void cut(int i, int j){
B newObject = new B(val);
...
newObject.array = this.array;
newObject = this;
}
}
The issue is that when I display the test object, it will only show me the original test object contents rather than newObject contents. Since this is a void method, I can't return an object. How do I reference the new object then? The last two lines for my cut method seem to have no effect at all. I know that ArrayList would be preferable for things like this, but this being a homework assignment we are forced to use arrays.
Now I know that arrays, once they're created cannot be changed in length.
This is true.
But the reference pointing to an array inside the B object instance can be changed (as you didn't declare it final):
public void cut(int i, int j){
Object[] newArray = new Object[len];
//... copying array content ...
this.array = newArray;
}
Beware of the thread safety issues such mutability causes. Mutable objects are usually frowned upon...
If the rules of assignment allow the use of class Arrays, this is what you can use instead of your own cut method:
newArray = Arrays.copyOfRange(oldArray, from, to);
In the method cut you create a new object of B then assign the the this reference to it. This has not effect as you have found because the this reference (the actual object) is not changed at all, The newObject is visible only inside this method, once it is terminated, the object is gone.
While you are in the same class B you dont have to create a new Object of it just for the purpose of altering the data it holds. You could just create a new array and copy the original content to it.
For copying the content you could use the method System#arraycopy. If you are not permited to use any jdk helping functionalities, then you could just loop over the original array and copy elements into the new one as you want.

Simple way to re-assign values in an array

I'm having trouble reassigning values in an array.
public static void main(String[] {
int[] arrayOfIntegers = new int[4];
arrayOfIntegers[0] = 11;
arrayOfIntegers[1] = 12;
arrayOfIntegers[2] = 13;
arrayOfIntegers[3] = 14;
arrayOfIntegers = {11,12,15,17};
}
Why am I unable to reassign values in the manner that I've attempted? If I can't do it, why can't I do it?
Why am I unable to reassign values in the manner that I've attempted? If I can't do it, why can't I do it?
Because Java doesn't have destructuring assignment like some other languages do.
Your choices are:
Assign a new array to the array variable as shown by Rohit and Kayaman, but that's not what you asked. You said you wanted to assign to the elements. If you assign a new array to ArrayOfIntegers, anything else that has a reference to the old array in a different variable or member will still refer to the old array.
Use System.arraycopy, but it involves creating a temporary array:
System.arraycopy(new int[]{11,12,15,17},
0,
ArrayOfIntegers,
0,
ArrayOfIntegers.length);
System.arraycopy will copy the elements into the existing array.
You need to provide the type of array. Use this:
arrayOfIntegers = new int[] {11,12,15,17};
From JLS Section 10.6:
An array initializer may be specified in a declaration (§8.3, §9.3, §14.4), or as part of an array creation expression (§15.10), to create an array and provide some initial values.
If you are trying to re-assign array elements in some range, you can't do that with direct assignment. Either you need to assign values at indices individually, or use the way as given by #TJCrowder in his answer.
The correct syntax is:
arrayOfIntegers = new int[]{11,12,15,17};

Why doesn't Java initialize Array Objects?

If one runs the following code in java:
public class Testing {
public static void main(String[] args) {
TestObject[] array = new TestObject[4];
//array[0] = new TestObject();
System.out.println(Arrays.asList(array));
}
}
class TestObject {
String aString;
public TestObject() {
aString = "This has been initialized.";
}
}
It will print (null, null, null, null), and if array[0] = new TestObject(); is uncommented, then the first object will have a memory address (and not be null). I'm just confused to as to why Java wouldn't automatically call the constructor for each Object in an array when the array is first initialized properly. What are the advantages of the way it works right now? Is it a space issue (as in it would be too costly to do so)?
Maybe I've just overlooked something silly or I'm simply mistaken. This is not directly related to a problem I'm having, so if it's the wrong forum I apologize.
What happens if you want to fill up your array with real objects that are subclasses of TestObject, or which are constructed with non-default constructors? In the real world, you rarely want an array with a bunch of identical objects.
With new TestObject[4] you create an array, wich can hold 4 references to TestObject.
So understand the difference between TestObject[] and TestObject:
TestObject[] is a reference store for TestObject - objects. If you create a List<TestObject> you'll have to fill up the list with references too.

Java class copying and arrays

I am having some issues withs objects within an array working as one object rather than seperately as intended. For example
boardArray = new object[4];
workingBoardArray = new object[4];
boardArray[0] = new board(boardA);
boardArray[1] = new board(boardB);
boardArray[2] = new board(boardC);
boardArray[3] = new board(boardD);
int selectedBoard = selectBoard(boardArray);
workingBoardArray[0] = boardArray[selectedBoard];
workingBoardArray[1] = boardArray[selectedBoard];
workingBoardArray[2] = boardArray[selectedBoard];
workingBoardArray[3] = boardArray[selectedBoard];
workingBoard[0].moveUp();
workingBoard[1].moveRight();
workingBoard[2].moveDown();
workingBoard[3].moveLeft();
This code creates a set of 4 boards, finds the desired board, copies that board into the working board array and then shifts each working board in a new direction. The problem is that the boards all act together. So if I do moveUp() on workingBoard[0], workingBoard[1] is also moved up. Is there any way around this, I feel like I am missing some fundamental understand or a really silly mistake.
I also tried using a copy constructer like
workingBoard[0] = new Board(boardArray[selectedBoard].getBoard);
Which retrieves all essential board information and creates a new board with it, but that doesnt seem to affect anything. Any help would be great
Look at this code:
int selectedBoard = selectBoard(boardArray);
workingBoardArray[0] = boardArray[selectedBoard];
workingBoardArray[1] = boardArray[selectedBoard];
workingBoardArray[2] = boardArray[selectedBoard];
workingBoardArray[3] = boardArray[selectedBoard];
That's setting all four array elements to be references to the same object. So yes, when you modify that object by calling moveUp() or whatever, that change will be visible via all the elements in the array.
It sounds like you may want to create a copy of the board each time. Depending on what your board type (which should be called Board to follow naming conventions) does, you may want to just implement Cloneable (quite possibly overriding the clone() method) or provide some other sort of copying operation.
Are you aware of the difference between a reference and an object in Java, and that the array only contains references, not objects? (Likewise any variable value will only be a reference rather than on object.) It's really important to understand how objects work in Java. For example:
StringBuilder a = new StringBuilder();
StringBuilder b = a;
a.append("Foo");
System.out.println(b); // Still prints "Foo"
Here we've got one StringBuilder object, even though there are two variables (a and b) whose values refer to that same object.
... copies that board into the working board array and then shifts each working board in a new direction.
Actually you don't copy the board. There is only one board instance which is referenced four times from workingBoardArray. To make copies of the board you have either to implement the clone method or provide a custom copy method.
For example if you implement the clone method you could write:
workingBoardArray[0] = boardArray[selectedBoard].clone();

Categories