1

So from what I understand when you pass the message copy to an array, a shallow copy is performed. Two objects should point to the same array and in theory, when you change one, you change the other. However, when I run this code, it acts like it's a deep copy. Each array is independent from each other. I make changes in the first array and the second array is unaffected. I make changes in the second array and the first array is unaffected. So how is copying actually working when I use copy?

| a b |

a := #('first' 'second' 'third').
b := a copy.
Transcript show: a = b;cr.
Transcript show: a == b;cr.
Transcript show: (a at: 1) == (b at: 1);cr.
b at: 1 put: 'newFirst'.
Transcript show: a;cr.
Transcript show: b;cr.
Transcript show: (a at: 1) == (b at: 1);cr.
a at: 2 put: '2nd'.
Transcript show: a;cr.
Transcript show: b;cr.
a := nil.
Transcript show: a;cr.
Transcript show: b;cr.

The results are:

true
false
true
#('first' 'second' 'third')
#('newFirst' 'second' 'third')
false
#('first' '2nd' 'third')
#('newFirst' 'second' 'third')
nil
#('newFirst' 'second' 'third')
On The Net Again
  • 265
  • 4
  • 16

3 Answers3

4

The copy operation indeed creates a shallow copy of the array. This means that a new array is created that refers to the same objects (“contents”) as the original array.

When you change your copy via at:put:, the original array remains unaffected because its references are not changed. Further, changing the references of the copy does not alter the contained objects themselves. Instead, the copy now simply refers to other objects ('newFirst' and '2nd' in your case). That is another reason why the contents of the original array is unaffected by putting new objects into the copy.

However, since this is a shallow copy, the strings in the original array are not copied. You can observe this as follows:

a := Array with: (String newFrom: 'first') with: 'second' with: 'third'.
b := a copy.
(a at: 1) replaceFrom: 1 to: 5 with: '1st  '.  "in-place modification"
Transcript show: a = b; cr. "true"
Transcript show: (a at: 1); cr. "1st  "
Transcript show: (b at: 1); cr. "1st  "
Transcript show: (a at: 1) == (b at: 1); cr. "true"

Here, the copy of the array is not changed, but one of the common elements of the original array and its copy is changed. If the copy operation had been deep, b at: 1 would not be affected by the change on the third line. It would point to a different String instead.


The String newFrom: in the code above is there to avoid modifying the String literal 'first'.

JayK
  • 3,006
  • 1
  • 20
  • 26
1

So from what I understand when you pass the message copy to an array, a shallow copy is performed. Two objects should point to the same array and in theory, when you change one, you change the other.

This is your mistake. It is true that copy will produce a shallow copy, but your understanding of a shallow copy is wrong. A shallow copy will give you a new instance of the object, but all instance variables will continue to point to the same objects. Effectively you are only creating one new object.

If you wanted variables 'a' and 'b' to point to the same object all you do is assign them to each other:

b := a.

This will let you modify 'b' and have the changes also affect 'a'.

To extend this concept further, you have the notion of deepCopy and deepDeepCopy, etc. The idea behind these variations is that they make new objects for the instance variables as well.

Technically, indexed objects like Array, OrderedCollection, String, etc don't use instance variables, but the concept is the same - deepCopy duplicates the objects pointed to by the copied object. deepDeepCopy would go another level deep and duplicate those pointed-to objects as well.

jcfbvfjfn
  • 53
  • 5
1

See if this makes more sense to you:

|a b|
a := #('first' 'second' 'third').
b := a copy.
(b at: 1) at: 1 put: $X.
a at: 2 put: '2nd'.
Transcript show: a; cr.
Transcript show: b; cr.

...produces the following output:

#('Xirst' '2nd' 'third')
#('Xirst' 'second' 'third')
Greg Buchholz
  • 900
  • 4
  • 17