How do I use collections in immutable class safely in Java? - java

I try to implement immutable class, and I see a rule stating "Perform cloning of objects in the getter methods to return a copy rather than the returning actual object reference".
I understand that when we use immutables there would be no change in copied / cloned collections returned from the getters. When we use custom classes, the change in original collection can be seen also cloned ( shallow copied ) collection return from the getters.
In below code, I could not understand the case :
I created two methods, one for return the original collection as courseList and one for shallow copy of the courselist.
I assigned two version to local references clist1 and clist2.
Then I changed the item in original list. I can see the change original list and copied list also when I reach them through student object. However the change cannot be seen throug the reference I pointed to the cloned course list before ! I think it should also be affected by the change. Why I cant see the change on previously copied version ? This is reference and I think it should be point the same memory area, I also check the result by another example below again.
I created a list containing StringBuilder. I appeded new strs to stringbuilder and then I can see the changed previously copied version of the list.
So, the main question, must I use the deep copy in immutable classes always ? Is this a wrong usage ? What would be the safe way to use collections in immutable classes ?
Thanks in advance.
ImmutableStudent.java
public final class ImmutableStudent {
public ImmutableStudent(String _name, Long _id, String _uni, ArrayList<String> _courseList){
name = _name;
id = _id;
uni = _uni;
courseList = new ArrayList<>();
_courseList.forEach( course -> courseList.add(course));
}
private final String name;
private final Long id;
private final String uni;
private final ArrayList<String> courseList;
public String getName() {
return name;
}
public Long getId() {
return id;
}
public String getUni() {
return uni;
}
public List<String> getCourseList() {
return courseList;
}
public List<String> getCourseListClone() {
return (ArrayList<String>)courseList.clone();
}
}
ImmutableHelper.java
public class ImmutableHelper {
public static void run(){
ArrayList<String> courseList = new ArrayList<>();
courseList.add("Literature");
courseList.add("Math");
String name = "Emma";
Long id = 123456L;
String uni = "MIT";
ImmutableStudent student = new ImmutableStudent(name, id, uni, courseList);
System.out.println(name == student.getName());
System.out.println(id.equals(student.getId()));
System.out.println(courseList == student.getCourseList());
System.out.println("Course List :" + student.getCourseList());
System.out.println("Course List Clone :" + student.getCourseListClone());
List<String> clist1 = student.getCourseList();
List<String> clist2 = student.getCourseListClone();
student.getCourseList().set(1, "Art");
System.out.println("Course List :" + student.getCourseList());
System.out.println("Course List Clone :" + student.getCourseListClone());
System.out.println("Previous Course List :" + clist1);
System.out.println("Previous Course List Clone :" + clist2);
// Check shallow copy using collections.clone()
ArrayList<StringBuilder> bList = new ArrayList<>();
StringBuilder a = new StringBuilder();
a.append("1").append("2").append("3");
StringBuilder b = new StringBuilder();
b.append("5").append("6").append("7");
bList.add(a);
bList.add(b);
ArrayList<StringBuilder> bListCp = (ArrayList<StringBuilder>)bList.clone();
System.out.println("Blist : " + bList);
System.out.println("BlistCp :" + bListCp);
a.append(4);
System.out.println("Blist : " + bList);
System.out.println("BlistCp :" + bListCp);
}
}
The Result
Course List :[Literature, Math]
Course List Clone :[Literature, Math]
Course List :[Literature, Math, Art]
Course List Clone :[Literature, Math, Art]
Previous Course List :[Literature, Math, Art]
Previous Course List Clone :[Literature, Math]
Blist : [123, 567]
BlistCp :[123, 567]
Blist : [1234, 567]
BlistCp :[1234, 567]

From the clone() Javadoc:
Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
What this means is that the reference returned by the clone method is actually a reference to a new instance of ArrayList that contains exactly the same elements as the original list. In an example:
// Original list is reference L1 and contains three elements A, B and C
L1 = [ A, B, C ]
// By doing L1.clone you get a reference to a new list L2
L2 = [ A, B, C ]
// When you add a new element to L1 you do not see the change in L2 because
// it is effectively a different list
L1 = [ A, B, C, D ]
L2 = [ A, B, C ]
// However, if you change one of the A, B or C's internal state then that
// will be seen when accessing that object through L2, since the reference
// kept in the lists are the same
L1 = [ A, B', C, D ]
L2 = [ A, B', C ]
For your question:
So, the main question, must I use the deep copy in immutable classes always ? Is this a wrong usage ? What would be the safe way to use collections in immutable classes ?
It depends on what you want to achieve. There are two scenarios:
Scenario A: You want the users of the class to receive an immutable view of the list (meaning no user can modify the list) but you want any changes that happen to the original list to be propagated through the immutable view.
Scenario B: You want all versions of the list to be immutable, even the internally kept one.
Both scenarios can be answered by using Collections.unmodifiableList, which states in the Javadoc:
Returns an unmodifiable view of the specified list. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.
The difference would be on where you use it. For Scenario A you would invoke the method in the getter, so that every caller would receive the unmodifiable view of the list but the class would still keep the modifiable view internally. For Scenario B you would store a reference to the result of calling unmodifiableList internally in the class. Note that on newer versions of Java you can also use List.copyOf to create an immutable list, which you could use for Scenario B.
From your description I believe what you are trying to achieve is scenario A.

Why I cant see the change on previously copied version ?
Precisely because you copied it! It's a copy - a different ArrayList object from the original, that just happens to contain the same elements.
This is reference and I think it should be point the same memory area
That is only true in the case of:
public List<String> getCourseList() {
return courseList;
}
which is why you see the change on clist1. With clone(), you are creating a new object, and allocating new memory. Sure, you are still returning a reference to an object, but it's not the same reference that courseList stores. It's a reference to the copy.
must I use the deep copy in immutable classes always ?
No, as long as the elements in the collection are immutable. The whole point of making a copy is so that users can't do things like this:
List<String> list = student.getCourseList();
list.add("New Course");
If getCourseList didn't return a copy, the above code would change the student's course list! We certainly don't want that to happen in an immutable class, do we?
If the list elements are immutable as well, then users of your class won't be able to mutate them anyway, so you don't need to copy the list elements.
Of course, all of this copying can be avoided if you just use an immutable list:
private final List<String> courseList;
public ImmutableStudent(String _name, Long _id, String _uni, ArrayList<String> _courseList){
name = _name;
id = _id;
uni = _uni;
courseList = Collections.unmodifiableList(_courseList)
};

Related

Copy references to new array list and delete in Java

I have one array list, studentsList, like the following:
class Student{
String name;
List<Subject> subjects;
}
I want to copy references to these objects in another list say studentsListCopy. When I make any changes in the Student objects in studentsListCopy, say change the name, it should also reflect in the original list but if I delete the student object from studentsListCopy, it should not get deleted from the original list. How can I achieve this ?
Create a new List with the Objects e.g. new ArrayList<>(subjects) and do as you please.
You have to simply create a new List (sub-implementation should be used) having your original list as an argument.
Any changes to the elements within any of the containers (original list or its copy), will affect the object itself as you are manipulating the referenced object and deleting or inserting elements within any of those containers won't affect the other as they are totally separated:
class Student {
String name;
List<Subject> subjects;
private void someMethod() {
// create a shallow copy
List<Subject> copy = new ArrayList<>(subjects);
// edit the contained elements
copy.get(0).setSomeProperty();
}
}
ArrayList<student> studentsListCopy = new ArrayList<student>();
for(int i=0;i<studentList;i++)
{
studentsListCopy.add(studentList[i])
}
were you perhaps looking for something like this?
Create an unmodifiable list from your list. In this way you can edit list elements' properties and you cannot remove elements from list. Like below:
List<Student> editStudentList = Collections.unmodifiableList(yourStudentList);

Why the static accessed list gets updated even if we assign this to local

public static void Method1(String a)
{
List<DataBean> list = new ArrayList<DataBean>();
list = StaticClass.masterList; // it has prepopulated list item
for (JavaBean bean: list) {
//Some condition and we call bean.setters
}
}
Why here the StaticClass.masterList gets updated in for loop I called the update on bean although?
The reference to the list is what you are copying and that doesn't get updated.
What can get updated in the object it references.
Note:
List<DataBean> list = new ArrayList<DataBean>();
Here list is not a List , it is just a reference to a list which is why you can assign it to a new object.
If you want to take a shallow copy of the masterList you can do.
List<DataBean> list = new ArrayList<DataBean>(StaticClass.masterList);
This way if you alter the list, it will not change the master. However, if you alter one of DataBeans, this will be visible. If you need a deep copy you can do
List<DataBean> list = new ArrayList<DataBean>();
for (DataBean db: StaticClass.masterList)
list.add(new DataBean(db));
Because list and StaticClass.masterList will reference the same object.
So if you call setters on any object in list you'll see the changes in StaticClass.masterList as well.

When reading from a java util List, why do all elements return the last element? (generalized)

Question: Why does the following print out:
ChildB___Parent of ChildB
ChildB___Parent of ChildB
Instead of what I think it should print out:
ChildA___Parent of ChildA
ChildB___Parent of ChildB
Short Self Contained Generalized Example of Issue:
import java.util.ArrayList;
import java.util.List;
public class StackExchangeQuestion1 {
public static void main(String[] args){
List<String[]> list = new ArrayList();
String[] pair = {"childID","parentID"}; //for readability this gets overwritten
//adding values to list
pair[0] = "ChildA";
pair[1] = "Parent of ChildA";
list.add(pair);
pair[0] = "ChildB";
pair[1] = "Parent of ChildB";
list.add(pair);
//checking values in list
for(int i=0;i<list.size();i++){
pair = list.get(i); //variable reuse for readability
System.out.println(pair[0]+"___"+pair[1]);
}
}}//end class and main
You are adding the same String[] reference twice to your list. Because of this, both elements in your list point to the same object. So, when you overwrote your array, you were really writing to the same piece of memory. The list was referencing this piece of memory twice, so you get 2 identical print statements.
Java will pass all non-primitive values by reference (that means it will pass a pointer to where a non-primitive object is stored in memory.)
If you were storing integers instead like so:
int a = 10;
list.add(a);
a = 20;
list.add(a);
Everything would be fine, because a is an interger, integers are primtive values, and primitive values are passed by value. This means the data is stored in a is copied to the list, not the point in memory a is held at.
Strings and arrays of any sort, however, are non-primitive.
To fix your code then, you need to pass 2 separate references to your list:
String[] pair1 = {"ChildA","Parent of ChildA"};
String[] pare2 = {"ChildB", "Parent of ChildB"};
//adding values to list
list.add(pair1);
list.add(pair2);
Edit:
You'd expressed concerns in comments about verbosity. First, I think your code isn't verbose. But if you're truly concerned about it here's a way to shorten it:
list.add(new String[]{"ChildA","Parent of ChildA"});
list.add(new String[]{"ChildB","Parent of ChildB"});
Because an ArrayList stores references, not objects. The reference pair always refers to the same object; you're simply adding that reference to your list twice.
Your string object array is overriding here. Your last change only will reflect in your string because list will store object's reference. If you want to get your assumed output create new objects every time before you set value.

Difference between Arrays.asList(array) and new ArrayList<Integer>(Arrays.asList(array))

What is the difference between
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); // Copy
List<Integer> list2 = Arrays.asList(ia);
, where ia is an array of integers?
I came to know that some operations are not allowed in list2. Why is it so?
How is it stored in memory (references / copy)?
When I shuffle the lists, list1 doesn't affect the original array, but list2 does. But still list2 is somewhat confusing.
How does ArrayList being upcasted to list differ from creating a new ArrayList?
list1 differs from (1)
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
First, let's see what this does:
Arrays.asList(ia)
It takes an array ia and creates a wrapper that implements List<Integer>, which makes the original array available as a list. Nothing is copied and all, only a single wrapper object is created. Operations on the list wrapper are propagated to the original array. This means that if you shuffle the list wrapper, the original array is shuffled as well, if you overwrite an element, it gets overwritten in the original array, etc. Of course, some List operations aren't allowed on the wrapper, like adding or removing elements from the list, you can only read or overwrite the elements.
Note that the list wrapper doesn't extend ArrayList - it's a different kind of object. ArrayLists have their own, internal array, in which they store their elements, and are able to resize the internal arrays etc. The wrapper doesn't have its own internal array, it only propagates operations to the array given to it.
On the other hand, if you subsequently create a new array as
new ArrayList<Integer>(Arrays.asList(ia))
then you create new ArrayList, which is a full, independent copy of the original one. Although here you create the wrapper using Arrays.asList as well, it is used only during the construction of the new ArrayList and is garbage-collected afterwards. The structure of this new ArrayList is completely independent of the original array. It contains the same elements (both the original array and this new ArrayList reference the same integers in memory), but it creates a new, internal array, that holds the references. So when you shuffle it, add, remove elements etc., the original array is unchanged.
Well, this is because ArrayList resulting from Arrays.asList() is not of the type java.util.ArrayList.
Arrays.asList() creates an ArrayList of type java.util.Arrays$ArrayList which does not extend java.util.ArrayList, but only extends java.util.AbstractList.
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); //copy
In this case, list1 is of type ArrayList.
List<Integer> list2 = Arrays.asList(ia);
Here, the list is returned as a List view, meaning it has only the methods attached to that interface. Hence why some methods are not allowed on list2.
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
Here, you are creating a new ArrayList. You're simply passing it a value in the constructor. This is not an example of casting. In casting, it might look more like this:
ArrayList list1 = (ArrayList)Arrays.asList(ia);
First of all, the Arrays class is a utility class which contains a number of utility methods to operate on Arrays (thanks to the Arrays class. Otherwise, we would have needed to create our own methods to act on Array objects)
asList() method:
asList method is one of the utility methods of Array class, it is a static method that's why we can call this method by its class name (like Arrays.asList(T...a) )
Now here is the twist. Please note that this method doesn't create new ArrayList object. It just returns a List reference to an existing Array object (so now after using asList method, two references to existing Array object gets created)
and this is the reason. All methods that operate on List object, may not work on this Array object using the List reference. Like
for example, Arrays size is fixed in length, hence you obviously can not add or remove elements from Array object using this List reference (like list.add(10) or list.remove(10);. Else it will throw UnsupportedOperationException).
any change you are doing using a list reference will be reflected in the exiting Arrays object (as you are operating on an existing Array object by using a list reference)
In the first case, you are creating a new Arraylist object (in the second case, only a reference to existing Array object is created, but not a new ArrayList object), so now there are two different objects. One is the Array object and another is the ArrayList object and there isn't any connection between them (so changes in one object will not be reflected/affected in another object (that is, in case 2, Array and Arraylist are two different objects)
Case 1:
Integer [] ia = {1,2,3,4};
System.out.println("Array : "+Arrays.toString(ia));
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); // new ArrayList object is created , no connection between existing Array Object
list1.add(5);
list1.add(6);
list1.remove(0);
list1.remove(0);
System.out.println("list1: " + list1);
System.out.println("Array: " + Arrays.toString(ia));
Case 2:
Integer [] ia = {1,2,3,4};
System.out.println("Array: " + Arrays.toString(ia));
List<Integer> list2 = Arrays.asList(ia); // Creates only a (new) List reference to the existing Array object (and NOT a new ArrayList Object)
// list2.add(5); // It will throw java.lang.UnsupportedOperationException - invalid operation (as Array size is fixed)
list2.set(0,10); // Making changes in the existing Array object using the List reference - valid
list2.set(1,11);
ia[2]=12; // Making changes in the existing Array object using the Array reference - valid
System.out.println("list2: " + list2);
System.out.println("Array: " + Arrays.toString(ia));
An explanation with documentation references would be better for someone looking for answer.
1. java.util.Arrays
This is a utility class with bunch of static methods to operate on given array
asList is one such static method that takes input array and returns an object of java.util.Arrays.ArrayList which is a static nested class that extends AbstractList<E> which in turn implements List interface.
So Arrays.asList(inarray) returns a List wrapper around the input array, but this wrapper is java.util.Arrays.ArrayList and not java.util.ArrayList and it refers to the same array, so adding more elements to the List wrapped array would affect the original one too and also we cannot change the length.
2. java.util.ArrayList
ArrayList has a bunch of overloaded constructors
public ArrayList() - // Returns arraylist with default capacity 10
public ArrayList(Collection<? extends E> c)
public ArrayList(int initialCapacity)
So when we pass the Arrays.asList returned object, i.e., List(AbstractList) to the second constructor above, it will create a new dynamic array (this array size increases as we add more elements than its capacity and also the new elements will not affect the original array) shallow copying the original array (shallow copy means it copies over the references only and does not create a new set of same objects as in original array)
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> namesList = Arrays.asList(names);
or
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> temp = Arrays.asList(names);
The above statement adds the wrapper on the input array. So the methods like add and remove will not be applicable on the list reference object 'namesList'.
If you try to add an element in the existing array/list then you will get "Exception in thread "main" java.lang.UnsupportedOperationException".
The above operation is readonly or viewonly.
We can not perform add or remove operation in list object.
But
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.ArrayList<String> list1 = new ArrayList<>(Arrays.asList(names));
or
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> listObject = Arrays.asList(names);
java.util.ArrayList<String> list1 = new ArrayList<>(listObject);
In the above statement you have created a concrete instance of an ArrayList class and passed a list as a parameter.
In this case, methods add and remove will work properly as both methods are from ArrayList class, so here we won't get any UnSupportedOperationException.
Changes made in the Arraylist object (method add or remove an element in/from an arraylist) will get not reflect in to the original java.util.List object.
String names[] = new String[] {
"Avinash",
"Amol",
"John",
"Peter"
};
java.util.List < String > listObject = Arrays.asList(names);
java.util.ArrayList < String > list1 = new ArrayList < > (listObject);
for (String string: list1) {
System.out.print(" " + string);
}
list1.add("Alex"); // Added without any exception
list1.remove("Avinash"); // Added without any exception will not make any changes in original list in this case temp object.
for (String string: list1) {
System.out.print(" " + string);
}
String existingNames[] = new String[] {
"Avinash",
"Amol",
"John",
"Peter"
};
java.util.List < String > namesList = Arrays.asList(names);
namesList.add("Bob"); // UnsupportedOperationException occur
namesList.remove("Avinash"); // UnsupportedOperationException
Note that, in Java 8, 'ia' above must be Integer[] and not int[]. Arrays.asList() of an int array returns a list with a single element. When using the OP's code snippet, the compiler will catch the issue, but some methods (e.g., Collections.shuffle()) will silently fail to do what you expect.
Many people have answered the mechanical details already, but it's worth noting:
This is a poor design choice, by Java.
Java's asList method is documented as "Returns a fixed-size list...". If you take its result and call (say) the .add method, it throws an UnsupportedOperationException. This is unintuitive behavior! If a method says it returns a List, the standard expectation is that it returns an object which supports the methods of interface List. A developer shouldn't have to memorize which of the umpteen util.List methods create Lists that don't actually support all the List methods.
If they had named the method asImmutableList, it would make sense. Or if they just had the method return an actual List (and copy the backing array), it would make sense. They decided to favor both runtime-performance and short names, at the expense of violating both the principle of least astonishment and the good object-oriented practice of avoiding UnsupportedOperationExceptions.
(Also, the designers might have made a interface ImmutableList, to avoid a plethora of UnsupportedOperationExceptions.)
package com.copy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class CopyArray {
public static void main(String[] args) {
List<Integer> list1, list2 = null;
Integer[] intarr = { 3, 4, 2, 1 };
list1 = new ArrayList<Integer>(Arrays.asList(intarr));
list1.add(30);
list2 = Arrays.asList(intarr);
// list2.add(40); Here, we can't modify the existing list,because it's a wrapper
System.out.println("List1");
Iterator<Integer> itr1 = list1.iterator();
while (itr1.hasNext()) {
System.out.println(itr1.next());
}
System.out.println("List2");
Iterator<Integer> itr2 = list2.iterator();
while (itr2.hasNext()) {
System.out.println(itr2.next());
}
}
}
Arrays.asList()
This method returns its own implementation of List. It takes an array as an argument and builds methods and attributes on top of it, since it is not copying any data from an array but using the original array this causes alteration in original array when you modify list returned by the Arrays.asList() method.
On the other hand, ArrayList(Arrays.asList());
is a constructor of ArrayList class which takes a list as argument and returns an ArrayList that is independent of list, i.e., Arrays.asList() in this case passed as an argument.
That is why you see these results.
1.List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); //copy
2.List<Integer> list2 = Arrays.asList(ia);
In line 2, Arrays.asList(ia) returns a List reference of inner class object defined within Arrays, which is also called ArrayList but is private and only extends AbstractList. This means what returned from Arrays.asList(ia) is a class object different from what you get from new ArrayList<Integer>.
You cannot use some operations to line 2 because the inner private class within Arrays does not provide those methods.
Take a look at this link and see what you can do with the private inner class:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Arrays.java#Arrays.ArrayList
Line 1 creates a new ArrayList object copying elements from what you get from line 2. So you can do whatever you want since java.util.ArrayList provides all those methods.
In response to some comments asking questions about the behaviour of Arrays.asList() since Java 8:
int[] arr1 = {1,2,3};
/*
Arrays are objects in Java, internally int[] will be represented by
an Integer Array object which when printed on console shall output
a pattern such as
[I#address for 1-dim int array,
[[I#address for 2-dim int array,
[[F#address for 2-dim float array etc.
*/
System.out.println(Arrays.asList(arr1));
/*
The line below results in Compile time error as Arrays.asList(int[] array)
returns List<int[]>. The returned list contains only one element
and that is the int[] {1,2,3}
*/
// List<Integer> list1 = Arrays.asList(arr1);
/*
Arrays.asList(arr1) is Arrays$ArrayList object whose only element is int[] array
so the line below prints [[I#...], where [I#... is the array object.
*/
System.out.println(Arrays.asList(arr1));
/*
This prints [I#..., the actual array object stored as single element
in the Arrays$ArrayList object.
*/
System.out.println(Arrays.asList(arr1).get(0));
// prints the contents of array [1,2,3]
System.out.println(Arrays.toString(Arrays.asList(arr1).get(0)));
Integer[] arr2 = {1,2,3};
/*
Arrays.asList(arr) is Arrays$ArrayList object which is
a wrapper list object containing three elements 1,2,3.
Technically, it is pointing to the original Integer[] array
*/
List<Integer> list2 = Arrays.asList(arr2);
// prints the contents of list [1,2,3]
System.out.println(list2);
Summary of the difference -
When a list is created without using the new, the operator Arrays.asList() method returns a wrapper which means:
you can perform an add/update operation.
the changes done in the original array will be reflected to List as well and vice versa.

copy constructor with ArrayList parameter

I'm trying to make a copy constructor for an object and one of the parameters is an ArrayList.
when creating the ArrayList object, I had in mind to use the ArrayList constructor where you can pass a collection as a parameter, but I'm not sure if this will work as a "pointer" to the arraylist or if this will create a whole new arraylist object
This is the code I have
public MyObject(MyObject other)
{
this.brands= other.brands;
this.count = other.count;
this.list = new ArrayList<Integer>(other.list); // will this create a new array list with no pointers to other.list's elements?
}
I'm not sure if this will work as a "pointer" to the arraylist or if
this will create a whole new arraylist object
When you use new, it will create a brand spanking new instance of ArrayList (this is what you have asked). But it will not also automatically create copies of its elements (which I think is what you are looking for). What this means is, if you change a mutable object in the new List, it will also change in the original List, if it is still around. This is because the List only holds references (kinda sorta but not exactly pointers) to the Objects in them, not the actual Objects themselves.
For example:
Person person = new Person("Rob"); // create a new Object
List<Person> myList = new ArrayList<Person>();
myList.add(person);
// Create another list accepting the first one
List<Person> myList2 = new ArrayList<Person>(myList);
for(Person p : myList2) {
p.setName("John"); // Update mutable object in myList2
}
person = new Person("Mary"); // stick another object into myList2
myList2.add(person);
for(Person p : myList2) {
System.out.println(p.getName()); // prints John and Mary as expected
}
for(Person p : myList) {
System.out.println(p.getName()); // prints only one result, John.
}
So you can see that the two Lists themselves can be modified independently, but when you use the constructor accepting another List, both will contain references to the same Person instances, and when the state of these objects change in one List, they will also change in the other (kinda sorta just like pointers).

Categories