I'm learning deep copy and shallow copy.
If we have two arrays:
int[]arr1={1,2,3,4,5};
int[]arr2={1,2,3,4,5};
Question: Both arrays point to the same references [1][2][3][4][5].
What will happen if I change arr1[2]?Does it changes arr2[2]?
When we pass an array (random array, not necessarily arr1 or arr2) from main into a method the compiler makes a shallow copy of the array to the method, right?
If the Java compiler was re-written to make and send a deep copy of the array data to the method.
What happens to the original array in the main? I think the original array may change according to the method and pass back into the main. I am not sure.
Question: Both arrays point to the same references [1][2][3][4][5].
Not exactly. Your arrays have identical content. That being said, as per your initialization, they data they are using is not shared. Initializing it like so would however:
int[]arr1={1,2,3,4,5};
int[] arr2 = arr1
What will happen if I change arr1[2]?Does it changes arr2[2]?
As per the answer to the above question, no it will not. However, if you were to change your initialization mechanism as per the one I pointed out, it would.
When we pass an array (random array, not necessarily arr1 or arr2)
from main into a method the compiler makes a shallow copy of the array
to the method, right?
Your method will receive a location where to find the array, no copying will be done.
If the Java compiler was re-written to make and send a deep copy of
the array data to the method. What happens to the original array in
the main? I think the original array may change according to the
method and pass back into the main. I am not sure.
If you were to create a deep copy of the array and send it to your method, and your method will work on that copy, then, the original array should stay the same.
Initalization of an array with values will always allocate new memory.
For "int[]arr1 ={1,2,3,4,5};"
The jvm will count the length of the initialization array and allocate the required amount of space in the memory. In this case jvm allocated memory for 5 integers.
when you do "int []arr2={1,2,3,4,5}", the same thing happens again ( jvm allocates memory for another 5 integers).
Thus changing arr1[2] will not reflect arr[2].
arr1[2]=10;
System.out.println(arr2[2]); // this will still print 3
If you wanted arr2 to point to the contents of arr1, you should do this:
int []arr2=arr1;
This would be a shallow copy. This makes an array reference object containing the same value as arr1. Now if you do :
arr1[2]=10;
System.out.println(arr2[2]); //this will print 10.
Now, if you want to do a deep copy of an array( Instead of duplicate initialization as you did), the right command would be:
int arr2[] = Arrays.copyOf(arr1, arr1.length);
This will behave like the first scenario ( your code - Changing arr1 will not affect arr2).
Because you have primitive types, you create independent arrays. To demonstrate deep and shallow copy:
MyObject a = new MyObject("a");
MyObject[] first = new MyObject[] {a};
MyObject[] second = new MyObject[] {a};
a.setName("b");
System.out.println(second[0].getName());
second[0].setName("c");
System.out.println(first[0].getName());
Related
I know that when I initialize a char array:
I have to
char[] b= new char[5];
or
char[] b= new char[5]({1,2,3,4,5});
why not like
ArrayList<Charset> list = new ArrayList<Charset>();
initialize array :
char[] b = new char[5](); ?
Why they are different? Is it one of java philosophical nature or some reasons behind it ?
If you've ever used C, then the answer is fairly simple. In C, the way you create arrays is by allocating a static length of memory on the stack that is large enough to contain the number of elements, and point to the first element with a pointer - or dynamic length of memory on the heap, and point to the first element with a pointer.
int a[5]; //stack, static allocation
int* a = (int*)malloc(sizeof(int)*5)); //heap, dynamic allocation
And in C++, the second version was changed to this, obviously because it's more obvious what is happening:
int* a = new int[5];
And they took this type of array creation over to Java.
int[] a = new int[5];
Arrays don't really work like typical objects, hence why even creating them and manipulating them with reflection uses a different Array class in order to manipulate the object. (see http://docs.oracle.com/javase/tutorial/reflect/special/arrayInstance.html )
ArrayLists are different, because they're just everyday classes like most things in java, so you initialize them with an actual constructor call:
List<T> = new ArrayList<T>();
Basically, arrays and classes just work in different ways.
That's is simply design of Java. ArrayList and Arrays are two different things. No need to be same declaration.
I guess the guys who created Java wanted to keep a syntax close to the C syntax. In Java, arrays are minimalist low-level objects, so their case is a bit particular.
ArrayList is a container, it's similar as Vector in C++, it can add and remove elements, but array can't change its size
Arrays and ArrayList are used for different purposes. If you need a fixed size collection of objects then go for array but if you need dynamically growing collection of objects then go for arraylist. In some way compiler need to know about what is your need, hence the syntax is different.
I have a 2D array called "playingField". Before any alterations take place, I make a tempField 2D array and set tempField = playingField.
After several modifications, which all work, I get to this point in the code:
else {
//at this point both playingField and tempField are unchanged still
boundsNormal = bounds.OutOfBounds(move.MovePlayer(playingField,trekker, direction, rowT, colT));
if(boundsNormal == true){
//for some reason, at this point, tempField gets reassigned to the new playingField (which I'm still not sure why *THAT* changes)
playingField = tempField;
MovePlayer is a method that modifies the 2D array it takes (in this case, playingField), and OutOfBounds returns true or false, given an array.
I can maybe understand why playingField is being changed, but have no idea why tempField should experience any alteration after I initialize the boundsNormal variable.
I make a tempField 2D array and set tempField = playingField.
What you're saying doesn't make sense. You're making an array variable called tempField, but if you do
tempField = playingField
then both variables now point to the same array. So they are both being changed because they are the same array.
To avoid this, you can generally use System.arrayCopy instead of =. However, for a 2-dimensional array it's a little more complicated to "deep copy" it, because you have an array of arrays.
(Note: More generally, when objects are mutable, you may need to "deep copy" or "deep clone" them instead of use =, to avoid this problem. Arrays are just one example of this general rule.)
playingField = tempField;
This line of code does not copy your array, it copies the reference. After this assignment both playingField and tempField represent the same array in the memory (the array represented by playingField before this is probably waiting to be garbage collected.) Therefore, if after this you change anything in the array represented by playingField, the changes will also be visible in tempField, as it is basically the same array now (not copied array, just the same array with two names).
int[][] array = new int[][] {...}
int[][] clone = array.clone();
I naively expected this to work. But it didn't - it cloned only the first dimension, and I had to go and clone the other dimension manually if I wanted a true clone. Note: the contents were properly copied. But when I changed clone[0][1], it reflected in array[0][1]
And while .clone() is known to perform a shallow clone, int[][] looks like a single object (if we don't know its internal implementation, at least)
Why is that behaviour chosen? Isn't int[][] referencing an array object, rather than just the first dimension of the array? And in what scenarios is cloning only the first dimension the desired behaviour?
Why is that behaviour chosen?
Consistency, most likely.
As you say, int[][] references an array object. It just so happens that the contents of each array element is another array, but that's just a detail. Java clones all arrays identically, and since the elements can be of any type it can't guarantee to perform a deep copy.
Hence clone() for arrays performs a shallow copy, so only the first dimension is cloned.
(In general it doesn't seem that there's a single "best" or "obvious" answer to the question of whether a clone implies deep or shallow copies. What the developer wants will depend on how each field is being used by the application, so a one-size-fits-all approach will naturally have limitations.)
This behavior is demonstrated because there is no true multi dimensional array.
Java achieves multiple dimensions by making arrays of arrays. Which means:
int[][] is actually Array<Array<int>>
Hence the clone would only copy the first dimension.
If you were trying with a 3 dimensional array, you would need to clone thrice.
The clone method is a so called shallow copy (see another stackoverflow answer on the clone method), which means that all the elements are copied by reference.
To get to what you want (also called deep cloning) you can either copy every single array yourself using the Arrays.copy recursively, so every (sub-)array you find will get a deep-clone, or check this stackoverflow answer on deep cloning.
Happy hacking :-)
I am actually not able to replicate the behavior you have mentioned. I can see some answers mentioning shallow copy as the root cause of the issue. Correct me if I am wrong, shallow copy just means that instead of copy of an object while cloning, we will get copy of the reference. A good explanation can be found here In Java, what is a shallow copy?
In this case shallow copy will only make sure that changes in original array get reflected in the cloned array but will not stop anything from being copied.
int[][] array = new int[][] {{1,2,3},{4,5,6}, {7,8,9}};
int[][] clone = array.clone();
//Try this to see magic of shallow copy
//array[2][1]=11;
for(int i=0;i<array.length;i++)
for(int j=0;j<array[i].length;j++)
System.out.println("array["+i+"]["+j+"]"+array[i][j]);
for(int i=0;i<clone.length;i++)
for(int j=0;j<clone[i].length;j++)
System.out.println("clone["+i+"]["+j+"]"+clone[i][j]);
For me the cloning is done perfectly, that is both arrays have same contents. The only reason I can think of for getting the issue that cloned array will not show some information is that we have actually modified original array after the cloning (shallow copy comes into play then).
BTW I used Java 1.6, I hope that is not an issue.
Edit: If we just need to understand why their is a shallow copy instead of deep copy of a multidimensional array. Let's look at 2 facts
While cloning, Objects always gets cloned as shallow copy (copy of reference of the object), only native types get a real copy. In Java, what is a shallow copy?
An array in java is actually an object Is an array an object in java
Now combining 1 and 2, we know that multidimensional array in Java is just an object, which has reference of other array objects.
Something like ArrayObj->{ArrayObj, ArrayObj, ArrayObj};
And because we have Objects inside objects (composition), we are supposed to get a shallow copy as per Java cloning rule.
I have encountered a problem in one of my Java projects, which causes bugs.
The problem sounds as following:
I have two arrays. Let's name them firstArray and secondArray. Object in this case is a seperate class created by me. It works, the array can be filled with objects of that type.
Object[] firstArray= new Object[];
Object[] secondArray = new Object[];
Now, when I get an element out of the first array, edit it and then copy it in the second array, the object from the first array gets altered too.
tempObj = firstArray[3];
tempObj.modifySomething();
secondArray[3] = tempObj;
Whenever I do this, the (in this case) 3rd element(actually 4th) of the first array gets the modifications. I don't want this. I want the first Array to remain intact, unmodified, and the objects I have extracted from the first array and then modified should be stored in the second so that the second array is actually the first array after some code has been run.
P.S. Even if I get the element from the first array with Array.get(Array, index) and then modify it, the element still gets modified in the first array.
Hopefully you understood what I wanted to say, and if so, please lend me a hand :)
Thank you!
You're going to have to create a new object.
The problem is the modifySomething call. When you do that, it alters the object on which it's called. So if you've only got one object (even by two names), you can't call modifySomething or they will both change.
When you say secondArray[3] = firstArray[3], you aren't creating a new object: you're just assigning a reference. Going through an intermediate temporary reference doesn't change that.
You'll need code that looks like this:
Object tempObj = firstArray[3].clone();
tempObj.modifySomething();
secondArray[3] = tempObj;
The clone() method must return a new object divorced from the original but having identical properties.
When you retrieve an element from your array, you have a reference to it. So if you modify it, the modification are shered through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new Object and use its constructor to initialize its fields.
The object extracted from the first array needs to be cloned to create a new instance that is seperate. Otherwise the modification will affect the object in the first array as it is the same object.
When you retrieve an element from your array, you get a reference to it. So if you modify it, the modification are shared through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new method which take in input your retrieved object and return a new one alike.
In Java, when you do this secondArray[3] = tempObj;, you actually put the reference to the array, not the real object
So firstArray[3] and secondArray[3] point to the same real object
What you need to do is to create a new object that is identical to your original object, and put the reference of the new object to your secondArray
It might worth to point out that default clone() function only does a shallow copy, so if you have mutable objects in your object's fields, it might cause some problems. Take a look at this article about how to do a deep copy
In java is it possible and if not how to calculate in c?
if it's really array (not a pointer), you can do sizeof(arr)/sizeof(*arr)
In Java, the length of a primitive array is array.length, while the length of an ArrayList (and most other collections) is arrayList.size()
In C, the length of an array is sizeof(array) / sizeof(array[0]), but this is nearly useless since you can't pass arrays as arguments (they degenerate to pointers). The normal way to find the size of an array in C is to pass it as an extra argument to the function, or sometimes to terminate it with a sentinel value (eg. strings are \0 terminated)
There is no way to calculate that. in C (not C++, which has std::array and std::vector) an array is transmitted as its pointer, which you might increase by some offset. So you really don't know the runtime size of an "array", except by some conventions.
In particular for formal arrays, there is no way to know the size of the actual array passed
e.g. as void f(int arr[]) { /*...*/ } unless you give a static dimension.
Likewise, with an external array declared as extern int xarr[]; you cannot get its dimension with sizeof(xarr)/sizeof(xarr[0]).
In C, there is no way of calculating the size of array if you have only a pointer to it. You must store it in separate variable.
In fact you HAVE TO keep the size of an array in separate variable because you have to allocate memory if you want to use dynamic-size array.
And if you want to use fixed-size array you know it's size by the time you're writing your code so why not use #define, variable or const to store it?
Java is totally different language than C and the philosophy of programming is different-you should always keep that in mind.
In Java, you should use array.length, look here for example: http://www.roseindia.net/help/java/a/java-array-length.shtml
Simply, there is no possibility if you recieve only a pointer. That's why main has an argc argument. It defines the number of entries in argv. If you have an array "datatype" (actually the same as a pointer, but the behaviour depends on the context), you can use
int[] arr = new int[10];
sizeof(arr)/sizeof(int) // or whatever type is contained in ``arr``