0

I am running this code:

public class testttt {
public static void main(String[] args){
    ArrayList<StringBuffer> listOne = new ArrayList <StringBuffer>();
       listOne.add(new StringBuffer("One"));
       listOne.add(new StringBuffer("Two"));
       listOne.add(new StringBuffer("Three"));

    ArrayList <StringBuffer> listTwo = new ArrayList <StringBuffer>(listOne);
       listOne.add(new StringBuffer("Four"));
       for (StringBuffer str : listTwo) {
          str.append("2");
       } 

    System.out.println("List One: " + listOne);
    System.out.println("List Two: " + listTwo);

}
}

I thought by having the "new ArrayList" declaration when initializing listTwo I would have created a distinct array that would be independent from listOne. However, the output is:

List One: [One2, Two2, Three2, Four]
List Two: [One2, Two2, Three2]

I have a suspicion that the listTwo initialization only copied over the references from listOne, but I thought it would have been handled by the "new ArrayList" section.

Any explanation would be greatly appreciated!

javanewbie
  • 117
  • 1
  • 9
  • Side note: it's recommended to use `StringBuilder` instead of `StringBuffer`, because the latter includes unnecessary synchronization overhead. – Mick Mnemonic Jul 26 '16 at 11:26
  • Unfortunately I am trying to self-learn Java through this textbook and there is no mention of StringBuilder, only StringBuffer :( – javanewbie Jul 26 '16 at 11:27
  • 1
    You have made a shallow copy of `listOne`; i.e. a different list containing references to the same objects. If you want `listTwo` to contain different `StringBuffer` objects, then add new `StringBuffer` instances to `listTwo` instead of using the copy constructor. – khelwood Jul 26 '16 at 11:29
  • They are functionally equivalent but `StringBuilder` was introduced in Java 5 as a drop-in replacement. – Mick Mnemonic Jul 26 '16 at 11:30
  • 2
    Sidee note on code quality: class names start with upper case, even for "TestClass1". – GhostCat Jul 26 '16 at 11:31

4 Answers4

1

By using the listOne for the listTwo construction, you are saying: "please create a new list, and then copy all elements from the first list into that second list".

And then of course, java is doing "call-by-value". This means: "copying" doesn't mean the creation of new StringBuffers. It means that both lists hold references to the same StringBuffer objects.

Thus when you iterate the second list, and modify members of the second list, you see the effects on the first list as well.

So, the "real" answer is: always understand the concepts you are using; the "real" message here isn't the explanation; but the fact that one core part of being a programmer is to be very precise about the code you write, and to really understand each and every tiny bit of statement your put in your code. Everything has a certain meaning; and if you don't know them, your code will keep "surprising" you.

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • However, in the subsequent for:each loop, only listTwo is explicitly modified. Why do these same changes extend to listOne as well? – javanewbie Jul 26 '16 at 11:25
  • thank you! just a quick question- I did the same thing but with an int[] and the same thing happens when I change, for example, the value of originalArray[0] (anything made to the copied array affects the original). This just surprises me a bit because I thought Java treated primitives and objects differently and would pass on primitives solely by value? – javanewbie Jul 26 '16 at 11:39
  • That sounds weird. But please understand: we don't do more-questions-and-answers in comments here. You could write up a new question; or do as I told you: do some more research. You are a beginner. Everything you can possible ask about ... has been asked here before. – GhostCat Jul 26 '16 at 11:40
  • okey dokey, thanks so much for your help on the original question :) – javanewbie Jul 26 '16 at 11:43
  • I think the first line should be `By using the listOne for the listTwo construction, you are saying: "please copy those elements from the first list into the second list wherever possible and create new elements which are new".` – Raman Sahasi Jul 26 '16 at 11:49
  • @RamanSahasi Thanks for the feedback; I reworded that sentence, although I think that your suggestion isn't too clear. – GhostCat Jul 26 '16 at 11:52
1

You first create a List (listOne) that contains instances of StringBuffer.

Then you create another List (listTwo) that contains the same instances of StringBuffer. However, this is a different list.

Then you add to listOne one additional element. You change just listOne, not listTwo.

Then you change each StringBuffer instance within listTwo. Since listOne and listTwo contain, among others, the same instances of StringBuffer, this change is visible in listOne as well.

Note: you didn't put copies of the StringBuffer instances into listTwo, but references to the same objects!

Keksi
  • 78
  • 7
1

Constructor and other methods perform shallow copy. As mentioned in this answer,

Shallow copies duplicate as little as possible. It tries to create a reference to same old object hence it creates a copy the collection structure, not the elements. With a shallow copy, two collections share the individual elements.

whereas a Deep copies everything and create duplicates. It copies all of the elements in the original collection duplicated.

You can use Collections utility class if you want to create a deep copy.

ArrayList <StringBuffer> listTwo = new ArrayList <StringBuffer>(listOne.size());
Collections.copy(listTwo , listOne);
Community
  • 1
  • 1
Raman Sahasi
  • 30,180
  • 9
  • 58
  • 71
0

When you use the copy constructor for ArrayList, you're telling the new list to copy the elements of the old list. What collections actually store in Java is references, and you get what's called a "shallow copy": copies of references, but the same shared objects.

When you then modify those objects, the changes are visible regardless of which list of references you use to get to them.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152