Supposing we have:
public class Test {
private List<String> mWorkList;
private List<String> mOriginalList;
public Test(List<String> list) {
mWorkList = list;
mOriginalList = list;
}
public void updateData(List<String> newList) {
mWorkList.clear();
mWorkList.addAll(newList);
}
}
I want to change only mWorkList but mOriginalList is changed too when I call updateData(List<String>).
So how should I do it to keep mOriginalList as initially assigned in constructor?
In Java you pass variables by their reference. This means that whenever you do an assignment like mWorkList = list the variable mWorkList will point to the same place in memory where list is currently pointing. If you do mOriginalList = list, then mOriginalList will also point to that position. I.e. all three lists refer the same object at that point.
If you want independent lists you need to copy all values from the list to a new list like this:
List<String> myList = new ArrayList<>(otherList);
This constructor of ArrayList automatically adds all values from the other list, here is its documentation.
Your code could then look like:
public Test(List<String> list) {
mWorkList = new ArrayList<>(list);
mOriginalList = new ArrayList<>(list);
}
Or if you don't intent to change mOriginalList you could also leave it as mOriginalList = list. But then bear in mind that if the user makes changes to list (which comes from outside of your class) they will also be reflected in your mOriginalList which could easily lead to nasty bugs.
You are setting both list with the same instance reference with
mWorkList = list;
mOriginalList = list;
You need to create a new instance for mOriginalList by duplicating the list. This can be done with one of the constructor of ArrayList(Collection).
mOriginalList = new ArrayList<>(list);
Please note that the instances in both list are the same, so if you update an instance in list, it will be changed in mOriginalList. If you want to break that link too, you will need to clone the list and his content.
You should create new List object in function Test, because all your lists refer to one variable
public Test(List<String> list) {
mWorkList = new ArrayList<>(list);
mOriginalList = new ArrayList<>(list);
}
Whenever you assign a object to another object only the reference is assigned (Shallow copy). You should call copy constructor to make a deep copy.
public class Test {
private List<String> mWorkList;
private List<String> mOriginalList;
public Test(List<String> list) {
mWorkList = new ArrayList<>(list);
mOriginalList = new ArrayList<>(list);
}
public void updateData(List<String> newList) {
mWorkList.clear();
mWorkList.addAll(newList);
}
}
In java every variable is a reference, so in this case it's normal that both variable changes if you change one of them.
To keep a copy you have to create a new object and clone the original one.
Related
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.
}
I am doing some experiments with java list. I have pass a list parameter to a simple method which add two strings into a new list. At the final I have assigned to the parameter list the list created in the method.
I have expected to have the following result:
[a, b] but I'm getting [c, d]
I have this code:
public class A {
public static void f(List<String> list){
List<String> list2 = new ArrayList<>();
list2.add("a");
list2.add("b");
list = list2;
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("c");
list.add("d");
f(list);
System.out.println(list);
}
}
Can anyone explain me what is happening in the background ?
Java assign method parameters 'by value' (in opposed to 'by reference' and 'by pointer').
When you call
void f(List<String> list)
You pass a pointer to a list. The pointer to the list is copied to another variable and the assignment
list = list2
assign 'list2' to a separate copy of the pointer. Means that by this assignment the original pointer remains unchanged.
You have not called f(list) in your main()
and even if you call it, then also you will get [c,d]
as you are not changing the passed list.
you are just changing the local list reference in f()
In your main function :
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("c");
list.add("d");
System.out.println(list);
}
You have created a list and added two elements to that list. And than you have simply printed that list, that is why you are always getting [c,d] as output. You have never called your function f().
Now in your function f() :
public static void f(List<String> list){
List<String> list2 = new ArrayList<>();
list2.add("a");
list2.add("b");
list = list2;
}
It expects a reference to a list as parameter.
step 1: List<String> list2 = new ArrayList<>(); creates a new list.
Than you add two elements to this new list.
And then in step list = list2; you are simply changing the reference of the list and now it points to list2. But you are no where manipulating the list created in the main method. So even if you call the function f(list) from main method, no changes will be reflected to your list in main method.
Suppose I have a class :
class Dummy{
public static ArrayList<String> varArray;
}
In another class I do this :
Class Dummy2{
void main()
{
ArrayList<String> temp = Dummy.varArray;
}
}
Now suppose in Dummy2 I add elements to temp. Will the changes be reflected in Dummy.varArray? Because this is what is happening in my program. I tried printing the address of the two and they both point to the same address. Didn't know static field worked like this. Or am I doing something wrong?
Its not about static. The statement ArrayList<String> temp = Dummy.varArray; means that both variables are referring to the same arraylist. As varArray is static, it will have only one copy.
You can read ArrayList<String> temp = Dummy.varArray; as, The variable temp is now referring to the ArrayList object which is being referred by Dummy.varArray
By the way, you need to initialize it using public static ArrayList<String> varArray = new ArrayList<String>(); before you perform any operations on it.
ArrayList<String> temp = Dummy.varArray; will take what is known as a reference copy (or a shallow copy). That is, they will point to the same object.
It does not take a deep copy. See How to clone ArrayList and also clone its contents?
Yes it is behaving correctly.
When you do this
ArrayList<String> temp = Dummy.varArray;
Both pointing to the same reference ,since temp not a new list, you just telling that refer to Dummy.varArray
To make them independent, create a new list
ArrayList<String> temp = new ArrayList<String>(); //new List
temp.addAll(Dummy.varArray); //get those value to my list
Point to note:
When you do this temp.addAll(Dummy.varArray) at that point what ever the elements in the varArray they add to temp.
ArrayList<String> temp = new ArrayList<String>(); //new List
temp.addAll(Dummy.varArray); //get those value to my list
Dummy.varArray.add("newItem");// "newitem" is not there in temp
The later added elements won't magically add to temp.
The static keyword means there will only be one instance of that variable and not one variable per instance.
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());
}