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);
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)
};
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 a Arraylist: ArrayList<PlayerBean> playerlist = new ArrayList<PlayerBean>();
from an Object that includes a String and an double (Name and points).
public class PlayerBean{private String name;private double points;}
However for one of my Spinners I want to show only the name (String) in my Arraylist.
How do I manage to delete(remove) the double(points)?
I tried this without any success any ideas?
I am using the swinger for android. any idea?
ArrayList<PlayerBean> playerlist = new ArrayList<PlayerBean>();
List<String> namesOnly = filterNames(playerlist);
private List<String> filterNames(ArrayList<PlayerBean> playerlist12) {
List<String> names = new ArrayList<String>();
for(PlayerBean b : playerlist12)
{
names.add(b.getName());
}
return names;
}
Your list contains PlayerBean objects and you can't temporarily delete member variables from objects. Thus you can't remove points from the list.
You could either use a List<String> instead or provide a spinner model that only displays the name. I assume you're using Swing, don't you?
Rather than removing them, why don't you make a new array List of String type, and assign all the names into this list. So you don't have any points.
This is a simple question but if I do
List<Object> list = getObjectsFromDatabase();
This would not be the correct way to handle this?
But this would?
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
Or if I had a class
public class ReportDisplayModel<T> {
public ReportDisplayModel(List<T> data) {
this.data = data;
}
public List<T> data;
}
And I wanted to set the data in this model I would use the constructor?
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
Instead of
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
Just need a clarification. Thanks.
It depends entirely on what getData() returns.
usually it is made to return Collections.unmodifiableList(result) so that clients can't modify the result.
if this result is not used anywhere else, and modifications to it doesn't mess with anything, it is fine to use the result as-is
It is rarely needed to use the copy constructor - use it when you are sure that modifying the data will impact some other component.
Regarding
List<Object> list = getObjectsFromDatabase();
vs
List<Object> firstList = getObjectsFromDatabase();
List<Object> list = new ArrayList<Object>(firstList);
either approach is fine. Depends on if you want list to refer to the list returned by getObjectsFromDatabase() or if you want it to refer to a copy of it.
If simply want to, say, print the database objects, the first approach is fine.
If you want to, say, filter out half of the database objects (i.e., remove objects from the list), and you can't say for sure that getObjectsFromDatabase() returns a mutable list, then you'll have to go with the second approach.
Regarding
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>(getData());
vs
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.data = getData();
I'd prefer the first method. Simply because I wouldn't want to worry about null pointer exceptions etc if I accidentally do something like
ReportDisplayModel<Object> model = new ReportDisplayModel<Object>();
model.printData();
model.data = getData();
I don't quite get your question, but I'll give it a try.
The main difference is that using the copy constructor creates a new independent copy of the list, i.e.
List<Object> firstList = getObjectsFromDatabase(); // firstList is the list returned by the database
List<Object> list = new ArrayList<Object>(firstList); //list is an independent copy of firstList
Now if you change firstList the list returned by getObjectsFromDatabase() would be changed as well (or would throw an exception if changes are not supported). On the other hand list could freely be changed without the original list being affected.
Avoid using the equal sign, because it breaks encapsulation (bad practice). Go for the copy constructor (best practice).