3

I have a List with custom objects. I want to create a deep copy of that List. Here is the class of the custom Object:

public class MyMemo {
    private List<Uri> imageUriList;
    private String commentText;

    public MyMemo(){
        imageUriList = new ArrayList<>();
    }

    public List<Uri> getImageUriList() {
        return imageUriList;
    }

    public void setImageUriList(List<Uri> imageUriList) {
        this.imageUriList = imageUriList;
    }

    public String getCommentText() {
        return commentText;
    }

    public void setCommentText(String commentText) {
        this.commentText = commentText;
    }
}

Now I have below situation:

List<MyMemo> parentList = new ArrayList<>();
List<MyMemo> copyList = new ArrayList<>(parentList);
parentList.get(currentMemoPosition).getImageUriList().removeAll(someOtherList.getImageUriList());
Log.e(TAG,"Total List: "+parentList.get(currentMemoPosition).getImageUriList().size()+" "+copyList.get(currentMemoPosition).getImageUriList().size());

But if I make any change to the parentList i.e either delete an item from it or add new item to it. copyList is also effected. How can I make sure copyList is not referring the same memory address.

Update:

As suggested to use clone(). But problem is I have a list in my custom object. How can I clone() them?

sagar suri
  • 4,351
  • 12
  • 59
  • 122

5 Answers5

8

When you create your first list:

List<MyMemo> parentList = new ArrayList<>();
parentList.add(customObject1);
parentList.add(customObject2);
parentList.add(customObject3);

parentList is a list of references to the custom objects.

When you copy the contents of parentList into copyList:

List<MyMemo> copyList = new ArrayList<>(parentList);

copyList now contains references to the same objects as parentList. If you change an object in parentList the object is also changed in copyList because they are the same object.

To create a deep copy of the list you need a way to copy one of your custom objects. You could implement a copy constructor or a clone method:

class CustomObject {
    private String item1;
    private String item2;

    public CustomObject(String item1, String item2) {
        this.item1 = item1;
        this.item2 = item2;
    }

    // copy constructor
    public CustomObject(CustomObject other) {
        this.item1 = other.getItem1();
        this.item2 = other.getItem2();
    }

    // clone
    public CustomObject clone() {
        CustomObject newObj = new CustomObject(this.getItem1(), this.getItem2());
        return newObj;
    }
}

Then you can copy an object like this:

CustomObject newObj = new CustomObject(existingObj);

or like this:

CustomObject newObj = existingObj.clone();

Now you can make a deep copy of your list using the copy constructor:

List<CustomObject> copyList = new ArrayList<>();
for(CustomObject obj : parentList) {
    copyList.add(new CustomObject(obj));
}

or the clone method:

List<CustomObject> copyList = new ArrayList<>();
for(CustomObject obj : parentList) {
    copyList.add(obj.clone());
}

I prefer to use a copy constructor. You can read a good article by Josh Block in Effective Java on the use of clone vs copy constructor.

Matt
  • 3,677
  • 1
  • 14
  • 24
  • 1
    Amazing explanation. – sagar suri Feb 02 '18 at 05:04
  • @Matt Lii mentions that Java 8 provides a new way to call the copy constructor using streams, lamdas and collectors here: https://stackoverflow.com/a/33507565/3796660. Any thoughts on this versus your copy constructor approach? – AJW Aug 04 '23 at 04:05
  • 1
    @AJW I like the streams approach but I haven't used Java in quite some time now. – Matt Aug 18 '23 at 12:26
  • @Matt Do you use Kotlin now rather than Java, or you haven't used either recently? – AJW Aug 18 '23 at 13:36
  • I've been working with Go for the last few years, things changed at work :) – Matt Aug 20 '23 at 07:22
6

IMO, add another arg constructor, which will do deep cloning for your Object.

public class MyMemo {
    MyMemo(MyMemo memo)
    {
       this.commentText = memo.getCommentText();
       this.imageUriList = new ArrayList<>();
       for (Uri uri : memo. getImageUriList())
       {
           this.imageUriList.add(new Uri(uri));
       }
    }
    // rest of your code
}

Now, iterate your parent List and clone each object

List<MyMemo> parentList = new ArrayList<>();
List<MyMemo> copyList = new ArrayList<>();
for (MyMemo memo : parentList)
{
   // create new instance of MyMemo and add to the list
   copyList.add(new MyMemo(memo));
}
Daniele
  • 668
  • 2
  • 10
  • 25
Ravi
  • 30,829
  • 42
  • 119
  • 173
  • Worked like charm :) – sagar suri Feb 02 '18 at 04:52
  • @Ravi Lii mentions that Java 8 provides a new way to call the copy constructor using streams, lamdas and collectors here: stackoverflow.com/a/33507565/3796660. Any thoughts on this versus your copy constructor approach? – AJW Aug 04 '23 at 04:06
0

all options below will make a shallow copy which means that changing a value in one array will affect the second one .It's no thread safe

1.

         List<Sth> a = new ArrayList<>();
         List<Sth> b = new ArrayList<>(a);

2.

        `Collection.copy(a,b)`

3.

ArrayList<Sth> b= (ArrayList) a.clone();

to create a new copy if a list you should iterate over this list and add elements to another list. this is a deep copy.

Bishoy Kamel
  • 2,327
  • 2
  • 17
  • 29
0

You can also create new list, create new object and use BeanUtils to copy properties,

for (MyMemo current : parentList) {
    MyMemo copyCurrent = new Data();
    BeanUtils.copyProperties(copyCurrent, current);
    copyList.add(copyCurrent);
}

This will give a deep copy of objects in the list.

0

In Android, if you try to make deep copy object type ArrayList then you able to copy them but when an object in 1st list made any changes then those changes reflected in the second list also.

for that, there is one solution,

  1. create one class with list property
  2. convert that class object to String using Gson().toJson()
  3. when you want that copied list again convert that String to the Class object.
 public class MyMemo {
     private List<DemoClass> demoList;
 }

 MyMemo m = new Mymemo();
 m.demoList(..,..,)

 1. String s = Gson().toJson(m)
 2.Mymemo m = Gson().fromJson(s,Mymemo.java)
Julian
  • 33,915
  • 22
  • 119
  • 174