Shallow copy for arrays, why can't it just do newArr = oldArr?

Say I have an array of integers, "orig"

I want to copy it, so I can not do this:

int[] shallow = orig; 

My professor said that for primitives, a shallow and deep copy is essentially the same, since we must copy over each index of the array. But setting the whole array is equal to another array, does the same thing, right?

I have a similar question with arrays of objects

That was my ideology.

 Book[] objArr2 = objArr1; 

But I was told that I would have to copy every index of the array, e.g.

 //for loop objArr2[i] = objArr1[i]; 

For shallow copy, is there really a difference between aligning arrays with another and individually copying each index of the array? (I understand that deep means you need to create new objects)

+4
source share
4 answers

I want to copy it, so I can not do this:

int [] underow = orig;

This is not a very shallow copy. A copy is a discrete entity that is similar to the original, but not the original. In your example, you have two links pointing to the same object. When you create a copy, you must have two resulting objects: the original and the copy.

Here, everything you do to change the shallow will happen with orig , since they both point to the same object.

Shading comes into play when the object you are comparing has links to other objects inside it. For example, if you have an array of integers and you create a copy, now you have two arrays that contain the same integer values:

 Original Array [0] [1] [2] [3] After copying: [0] <--- Original [0] [1] [1] [3] [2] [4] Copy ---> [3] 

However, if you had an array consisting of objects (say objArr1 and objArr2 )? When you make a shallow copy, now you have two new array objects, but each corresponding record between the two arrays points to the same object (since the objects themselves were not copied, but only links).

 Original Array: [0:]----> [object 0] [1:]----> [object 1] [2:]----> [object 2] [3:]----> [object 3] 

After copying (note how the corresponding locations point to the same instances):

 Original -> [0:]----> [object 0] <----[:0] <- Copy [1:]----> [object 1] <----[:1] [2:]----> [object 2] <----[:2] [3:]----> [object 3] <----[:3] 

Now, if you change objArr1 , replacing the record or deleting the record, then < objArr2 does not work. However , if you change the object in objArr1[0] , which is reflected in objArr2[0] , as these locations point to the same object. Thus, in this case, although the container objects themselves are different, they contain references to the same object.

When you make a deep copy, you will have two new arrays, where each corresponding location points to different instances. Thus, you almost completely copy objects.

My professor said that for primitives, a shallow and deep copy is essentially the same, since we must copy over each index of the array.

The important difference is that when you copy an array of primitives, you exactly copy the values. Every time you get a new primitive. However, when you have an array of objects, you do have an array of object references. Therefore, when you create a copy, all you have done is create a new array that has copies of the links in the original array. However, these new copies of the links still point to the same corresponding objects. This is what is known as a shallow copy. If you have deeply copied the array, the objects referenced by each individual location will also be copied. So you will see something like this:

 Original -> [0:]----> [object 0] Copy -> [0:]----> [copy of object 0] [1:]----> [object 1] [1:]----> [copy of object 1] [2:]----> [object 2] [2:]----> [copy of object 2] [3:]----> [object 3] [3:]----> [copy of object 3] 

But setting the whole array is equal to another array, does the same thing, right?

No no. Here you simply create a new reference to an existing array:

 arr1 -> [0, 1, 2, 3, 4] 

Now let's say that you did arr2 = arr1 . You have:

 arr1 -> [0, 1, 2, 3, 4] <- arr2 

So here arr1 and arr2 point to the same array. Therefore, any modification performed using arr1 will be displayed when accessing the array using arr2 , since you are looking at the same array. This does not happen when you make copies.

+14
source

The problem is that you need to think of the array as an object. What you saved in objArr1 is a memory address that refers to the beginning of the array. So, for example, if the array objArr1 is stored at 0x1234578, then the value of objArr1 is 0x1234578, and when you say

 objArr2 = objArr1; 

You say that objArr2 should be 0x1234578. Now, if you change the element to one of the array stored in 0x1234578, then whether you reference it with objArr1[1] or objArr2[1] , the value will be the same. And the same is true if you try to change one of them: everything that you do with one will be reflected in the other, because they point to the same place. For example, the following code will be true

 objArr2 = objArr1; objArr2[0] = 5; objArr1[0] = 6; System.out.println(objArr2[0]); //prints "6" 

Sometimes this behavior is useful, but it does not lead to copying.

+1
source

For primitives

Consider the following example:

 public class Example { //this class just uses the reference as you suggest public static class ArrayEater { private final int[] food; public ArrayEater(final int[] food) { this.food = food; // references same array, does not crate copy } public void eat() { for (int index = 0; index < food.length; index++) { final int bite = food[index]; if (bite == 0) { System.out.println("No food at position " + index); } else { System.out.println("Eating " + bite + " from position " + index); } food[index] = 0; } } } //this class makes an actual copy public static class ArrayCopyThenEatEater { private final int[] food; public ArrayCopyThenEatEater(final int[] food) { this.food = new int[food.length]; // creates new array for (int index = 0; index < food.length; index++) { //copies over the values this.food[index] = food[index]; } } public void eat() { for (int index = 0; index < food.length; index++) { final int bite = food[index]; if (bite == 0) { System.out.println("No food at position " + index); } else { System.out.println("Eating " + bite + " from position " + index); } food[index] = 0; } } } public static void main(String[] args) { int[] originalArray = {1,3,6,9}; ArrayEater eater = new ArrayEater(originalArray); eater.eat(); eater.eat(); for (int index = 0; index < originalArray.length; index++) { System.out.println("Original array has value of " + originalArray[index] + " at position " + index); } originalArray = new int[]{1,3,6,9}; ArrayCopyThenEatEater copyEater = new ArrayCopyThenEatEater(originalArray); copyEater.eat(); copyEater.eat(); for (int index = 0; index < originalArray.length; index++) { System.out.println("Original array has value of " + originalArray[index] + " at position " + index); } } } 

If you look at the main method, you will see that the originalArray is created and passed to the two "eaters". One of the eaters simply refers to the original Array, as you propose to do, the other eater creates the actual copy (as your professor said, this copy is shallow and deep for the primitive).

Running this creates the following output:

 Eating 1 from position 0 Eating 3 from position 1 Eating 6 from position 2 Eating 9 from position 3 No food at position 0 No food at position 1 No food at position 2 No food at position 3 Original array has value of 0 at position 0 <-- here we see that the eater ate the original!! Original array has value of 0 at position 1 Original array has value of 0 at position 2 Original array has value of 0 at position 3 Eating 1 from position 0 Eating 3 from position 1 Eating 6 from position 2 Eating 9 from position 3 No food at position 0 No food at position 1 No food at position 2 No food at position 3 Original array has value of 1 at position 0 <-- here we see that the eater did not eat the original!! Original array has value of 3 at position 1 Original array has value of 6 at position 2 Original array has value of 9 at position 3 

Looking above, you will see that your approach causes the original content of the array to be overwritten, while the copy method proposed by your professor does not modify the original array.


For non-primitives

Here we have the Food class, which was either eaten or not. We have three eaters, one for reference copy, one shallow copy and one deep copy:

 public class Example { public static class Food implements Cloneable { private boolean eaten = false; public void eat() { eaten = true; } public boolean isEaten() { return eaten; } public Food clone() { try { return (Food) super.clone(); } catch (CloneNotSupportedException e) { return null; //we won't get here } } } public static class ReferenceEater { private final Food[] food; public ReferenceEater(final Food[] food) { this.food = food; // references same array, does not crate copy } public void eat() { for (int index = 0; index < food.length; index++) { final Food bite = food[index]; if (bite.isEaten()) { System.out.println("No food at position " + index); } else { System.out.println("Eating from position " + index); bite.eat(); } } } } public static class ShallowEater { private final Food[] food; public ShallowEater(final Food[] food) { this.food = new Food[food.length]; // creates new array for (int index = 0; index < food.length; index++) { this.food[index] = food[index]; //shallow copy still references same elements! } } public void eat() { for (int index = 0; index < food.length; index++) { final Food bite = food[index]; if (bite.isEaten()) { System.out.println("No food at position " + index); } else { System.out.println("Eating from position " + index); bite.eat(); } } } } public static class DeepEater { private final Food[] food; public DeepEater(final Food[] food) { this.food = new Food[food.length]; // creates new array for (int index = 0; index < food.length; index++) { this.food[index] = food[index].clone(); //deep copy also copies the elements! } } public void eat() { for (int index = 0; index < food.length; index++) { final Food bite = food[index]; if (bite.isEaten()) { System.out.println("No food at position " + index); } else { System.out.println("Eating from position " + index); bite.eat(); } } } } public static void main(String[] args) { Food[] originalArray = {new Food(), new Food(), new Food()}; ReferenceEater referenceEater = new ReferenceEater(originalArray); referenceEater.eat(); referenceEater.eat(); for (int index = 0; index < originalArray.length; index++) { System.out.println("Food at position " + index + " has been eaten? " + originalArray[index].isEaten()); } originalArray = new Food[]{new Food(), new Food(), new Food()}; ShallowEater shallowEater = new ShallowEater(originalArray); shallowEater.eat(); shallowEater.eat(); for (int index = 0; index < originalArray.length; index++) { System.out.println("Food at position " + index + " has been eaten? " + originalArray[index].isEaten()); } originalArray = new Food[]{new Food(), new Food(), new Food()}; DeepEater deepEater = new DeepEater(originalArray); deepEater.eat(); deepEater.eat(); for (int index = 0; index < originalArray.length; index++) { System.out.println("Food at position " + index + " has been eaten? " + originalArray[index].isEaten()); } } } 

Note that a deep copy also copies the individual elements of the array.

Now let's look at the result:

 Eating from position 0 Eating from position 1 Eating from position 2 No food at position 0 No food at position 1 No food at position 2 Food at position 0 has been eaten? true Food at position 1 has been eaten? true Food at position 2 has been eaten? true Eating from position 0 Eating from position 1 Eating from position 2 No food at position 0 No food at position 1 No food at position 2 Food at position 0 has been eaten? true Food at position 1 has been eaten? true Food at position 2 has been eaten? true Eating from position 0 Eating from position 1 Eating from position 2 No food at position 0 No food at position 1 No food at position 2 Food at position 0 has been eaten? false Food at position 1 has been eaten? false Food at position 2 has been eaten? false 

Here we see that, as in the case of primitives, the reference copy made the original be eaten again. But, looking at the shallow copy that we get in the same way as the deep / shallow copy for the primitives above, the food was already eaten in the original this time (unlike the primitives!). This is because, although we created a new array, we passed references to the same instances of Food. Finally, with a deep copy, we see that the original foodstuffs from the array were not eaten, as the eaters ate clones of these items.

+1
source

Your examples use arrays, so I'll just use them as an example. Arrays are really β€œshared” blocks of memory (based on type). The value stored as int [] a is the starting memory address for this array. When you execute int [] a = someOtherArray, you assign it the address of another memory cell, not the values ​​that they store. Therefore, you need to go through element by element and assign the values ​​stored in each place.

0
source

Source: https://habr.com/ru/post/1443313/


All Articles