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).
Related
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)
};
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have this, three different array lists:
ArrayList<type1> alista = new ArrayList<>();
ArrayList<type2> blistb = new ArrayList<>();
ArrayList<type3> clistc = new ArrayList<>();
I then create a new array list and put those 3 array lists into it:
ArrayList<Object> all_lists = new ArrayList<Object>();
all_lists.add(alista);
all_lists.add(blistb);
all_lists.add(clistc);
How do I add or remove objects inside all_lists: alista, blistb and clistc? I want to pass this all_lists back and forth between my methods inside my main(). I know doing it this way is probably wrong but I'd like to get this to work before I fix that with doing it better.
For example how do I get blistb out of that, then add one type of type2 I've created and then remove one type2 (from blistb). And then put it back (or create new all_lists?) into all_lists?
If I understand correctly, you want to be able to pass around all your data and make changes to the lists. First I would change the type of your lists to the List interface. This does not the function of your code but makes it easier if you'd want to change the List implementation in the future and it also saves some characters:
List<Type1> alista = new ArrayList<>();
List<Type2> blistb = new ArrayList<>();
List<Type3> clistc = new ArrayList<>();
We'll do the same with all_lists and at the same time, change its generic type to List to make life easier later:
List<List<Object>> allLists = new ArrayList<Object>();
(If Type1, Type2 and Type3 have some common ancestor that they all extend from, change the Object above to that type.)
To get one of your lists, blistb for instance, you need to know it's position in all_lists:
List<Type2> blistbReference = allLists.get(1); // Index starts from 0
This will probably be hard to maintain and to keep track of. There is also a risk that some future code change changes the order which will cause errors and headaches.
A better way to handle your lists would be to wrap them in a data object:
public class AllLists {
private List<Type1> alista;
private List<Type2> blistb;
private List<Type3> clistc;
public AllLists(List<Type1> alista, List<Type2> blistb, List<Type3> clistc) {
this.alista = alista;
this.blistb = blistb;
this.clistc = clistc;
}
public List<Type1> getAlista() {
return alista;
}
public List<Type1> getBlistb() {
return blistb;
}
public List<Type1> getClistc() {
return clistc;
}
}
// ......
AllLists allLists = new AllLists(alista, blistb, clistc);
You can now get your lists easily from the AllLists object and modify them as you like.
Type2 t = new Type2;
allLists.getBlistb().add(t);
You don't need to "put the list back" since Java is pass by reference. When you get the blistb from allLists, you are getting a reference to the same list object. Any changes to the reference is a change to the original list.
I changed type and variable names to be more standardized.
You should declare all_lists as a List of Lists; then, you can directly add and remove items to the Lists at specific indexes.
List<List> all_lists = new ArrayList<>();
all_lists.add(new ArrayList<A>(Arrays.asList(new A(1), new A(2), new A(3))));
all_lists.add(new ArrayList<B>(Arrays.asList(new B(4), new B(5))));
all_lists.add(new ArrayList<C>(Arrays.asList(new C(6))));
all_lists.get(1).add(new B(3));//add element to B List
You just need to keep track of which array is which:
in:
ArrayList<Object> all_lists = new ArrayList<Object>();
all_lists.add(alista);
all_lists.add(blistb);
all_lists.add(clistc);
all_lists.get(0) is alista
all_lists.get(1) is blistb
all_lists.get(2) is clistc
You remove the object from blistb by knowing which index it is in that list and removing it like normal.
all_lists.get(1).remove(2);
all_lists should be declared as ArrayList<ArrayList> (using raw-type which would require #SuppressWarnings("unchecked")).
#SuppressWarnings("unchecked")
ArrayList<ArrayList> all_lists = new ArrayList<>();
all_lists.add(alista);
all_lists.add(blistb);
all_lists.add(clistc);
clistc.add(new type3());
all_lists.get(0).add(new type1()); // add type1 object to alista
all_lists.get(1).add(new type2()); // add type2 object to blistb
all_lists.get(2).remove(0); // remove the 1st element of clistc
However, it would be better and more type-safe object-oriented way to create a wrapper class instead of all_list:
class ListWrapper {
private List<type1> aListA;
private List<type2> bListB;
private List<type3> cListC;
// constructor(s), getters/setters
public void addType1(type1 t1) {
aListA.add(t1);
}
// etc.
}
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.
Simply put, I have a method with an ArrayList parameter. In the method I modify the contents of the ArrayList for purposes relevant only to what is returned by the method. Therefore, I do not want the ArrayList which is being passed as the parameter to be affected at all (i.e. not passed as a reference).
Everything I have tried has failed to achieve the desired effect. What do I need to do so that I can make use of a copy of the ArrayList within the method only, but not have it change the actual variable?
Even if you had a way to pass the array list as a copy and not by reference it would have been only a shallow copy.
I would do something like:
void foo(final ArrayList list) {
ArrayList listCopy = new ArrayList(list);
// Rest of the code
}
And just work on the copied list.
You can create a copy of the ArrayList using ArrayList's copy constructor:
ArrayList copy = new ArrayList(original);
But if the elements of the list are also objects, then you must be aware that modifying a member of the copy will also modify that member in the original.
You could pass Collections#unmodifiableList(yourList) in order to send an unmodifiable copy of your list. By the way, your List<Whatever> is passed by value since Java always pass by value, note that in foo(List<Whatever> list) method you can not modify the list value but you can modify its contents.
public class MyClass {
List<Whatever> list = new ArrayList<Whatever>();
public void bar() {
//filling list...
foo(Collections.unmodifiableList(list));
}
public void foo(List<Whatever> list) {
//do what you want with list except modifying it...
}
}
You could use the .clone method or a CopyOnWriteArrayList to make a copy, thereby not impacting the original.
Try this in you method :
void method(List<Integer> list) {
List copyList = new ArrayList<Integer>();
copyList.addAll(list); // This will create a copy of all the emlements of your original list
}
I'm not sure on why, even after new ArrayList<MyObj>(old) the object was still changing reference in places it wasn't supposed to. So I had to instantiate a new copy of the objects inside.
I made a copy constructor like the one on the ArrayList and did like
newArray = new ArrayList<MyObj>();
for (int i = 0; i < oldArray.size(); i++) {
newArray.add(new MyObj(ondArray.get(i)));
}
Just hope to help someone else if the answer from Avi is not enough in your case, like mine with a code too messy to even understand =P
Just clone it.
public ArrayList cloneArrayList(ArrayList lst){
ArrayList list = new ArrayList();
for (int i=0; i<lst.size(); i++){
list.add(lst.get(i));
}
return list;
}
Add suggested in the comments, you can also use
ArrayList copy = new ArrayList(original);
and also
ArrayList copy = new ArrayList();
copy.addAll(original);
On the lines of the existing answers but using the ArrayList API. You can use subList(fromIndex, toIndex) method. It explicitly creates a view of the list with only desired elements (of course, in sequence). Here, even if you modify the view with add/remove etc operations, it won't change the original list. It saves you from explicitly creating a copy.
Something like this:
public void recursiveMethod(List<Integer> list) {
if(base)
return;
recursiveCall(list);
// following will just create a tail list but will not actually modify the list
recursiveCall(list.subList(1, list.size());
}
I have some data structures, and I would like to use one as a temporary, and another as not temporary.
ArrayList<Object> myObject = new ArrayList<Object>();
ArrayList<Object> myTempObject = new ArrayList<Object>();
//fill myTempObject here
....
//make myObject contain the same values as myTempObject
myObject = myTempObject;
//free up memory by clearing myTempObject
myTempObject.clear();
now the problem with this of course is that myObject is really just pointing to myTempObject, and so once myTempObject is cleared, so is myObject.
How do I retain the values from myTempObject in myObject using java?
You can use such trick:
myObject = new ArrayList<Object>(myTempObject);
or use
myObject = (ArrayList<Object>)myTempObject.clone();
You can get some information about clone() method here
But you should remember, that all these ways will give you a copy of your List, not all of its elements. So if you change one of the elements in your copied List, it will also be changed in your original List.
originalArrayList.addAll(copyArrayList);
Please Note: When using the addAll() method to copy, the contents of both the array lists (originalArrayList and copyArrayList) refer to the same objects or contents. So if you modify any one of them the other will also reflect the same change.
If you don't wan't this then you need to copy each element from the originalArrayList to the copyArrayList, like using a for or while loop.
There are no implicit copies made in java via the assignment operator. Variables contain a reference value (pointer) and when you use = you're only coping that value.
In order to preserve the contents of myTempObject you would need to make a copy of it.
This can be done by creating a new ArrayList using the constructor that takes another ArrayList:
ArrayList<Object> myObject = new ArrayList<Object>(myTempObject);
Edit: As Bohemian points out in the comments below, is this what you're asking? By doing the above, both ArrayLists (myTempObject and myObject) would contain references to the same objects. If you actually want a new list that contains new copies of the objects contained in myTempObject then you would need to make a copy of each individual object in the original ArrayList
Came across this while facing the same issue myself.
Saying arraylist1 = arraylist2 sets them both to point at the same place so if you alter either the data alters and thus both lists always stay the same.
To copy values into an independent list I just used foreach to copy the contents:
ArrayList list1 = new ArrayList();
ArrayList list2 = new ArrayList();
fill list1 in whatever way you currently are.
foreach(<type> obj in list1)
{
list2.Add(obj);
}
Supopose you want to copy oldList into a new ArrayList object called newList
ArrayList<Object> newList = new ArrayList<>() ;
for (int i = 0 ; i<oldList.size();i++){
newList.add(oldList.get(i)) ;
}
These two lists are indepedant, changes to one are not reflected to the other one.
Lets try the example
ArrayList<String> firstArrayList = new ArrayList<>();
firstArrayList.add("One");
firstArrayList.add("Two");
firstArrayList.add("Three");
firstArrayList.add("Four");
firstArrayList.add("Five");
firstArrayList.add("Six");
//copy array list content into another array list
ArrayList<String> secondArrayList=new ArrayList<>();
secondArrayList.addAll(firstArrayList);
//print all the content of array list
Iterator itr = secondArrayList.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}
In print output as below
One
Two
Three
Four
Five
Six
We can also do by using clone() method for which is used to create exact copy
for that try you can try as like
**ArrayList<String>secondArrayList = (ArrayList<String>) firstArrayList.clone();**
And then print by using iterator
**Iterator itr = secondArrayList.iterator();
while (itr.hasNext()) {
System.out.println(itr.next());
}**
You need to clone() the individual object. Constructor and other methods perform shallow copy. You may try Collections.copy method.
Straightforward way to make deep copy of original list is to add all element from one list to another list.
ArrayList<Object> originalList = new ArrayList<Object>();
ArrayList<Object> duplicateList = new ArrayList<Object>();
for(Object o : originalList) {
duplicateList.add(o);
}
Now If you make any changes to originalList it will not impact duplicateList.
to copy one list into the other list, u can use the method called
Collection.copy(myObject myTempObject).now after executing these line of code u can see all the list values in the myObject.
Copy of one list into second is quite simple , you can do that as below:-
ArrayList<List1> list1= new ArrayList<>();
ArrayList<List1> list2= new ArrayList<>();
//this will your copy your list1 into list2
list2.addAll(list1);
Here is a workaround to copy all the objects from one arrayList to another:
ArrayList<Object> myObject = new ArrayList<Object>();
ArrayList<Object> myTempObject = new ArrayList<Object>();
myObject.addAll(myTempObject.subList(0, myTempObject.size()));
subList is intended to return a List with a range of data. so you can copy the whole arrayList or part of it.
Suppose you have two arraylist of String type .
Like
ArrayList<String> firstArrayList ;//This array list is not having any data.
ArrayList<String> secondArrayList = new ArrayList<>();//Having some data.
Now we have to copy the data of second array to first arraylist like this,
firstArrayList = new ArrayList<>(secondArrayList );
Done!!
The simplest way is:
ArrayList<Object> myObject = new ArrayList<Object>();
// fill up data here
ArrayList<Object> myTempObject = new ArrayList(myObject);