How to Clone a Generic List in Java

How do I clone a generic List in Java?

ArrayList newArrayList = (ArrayList) oldArrayList.clone();

Java generics, copy one list to another

To answer your first question, are these two function signatures different in java? No! The quickest way to test this is by calling them both copy and verifying that the compiler complains of duplicate methods.

public static <T> void copy(List<T> dst, List<? extends T> src)
public static <T> void copy(List<? super T> dst, List<? extends T> src)

Your second question is, in what case would one work and the other wouldn't?

Well essentially we're talking about the first parameter, dst since it is the only difference between the two.

For any given T, in the case of copy2, the first parameter would have to a superclass of T or T itself and the second parameter would have to a subclass of T or T itself. The compiler interprets to mean that the first parameter generic type must be T, and the second parameter generic type is simply an extension of T. In fact, if you try, you'll see that the compiler will only take the same type as generic of first parameter or a derived class of that:

public static <T> void copy2(List<? super T> dst, List<? extends T> src) {
// Copy
}

public static void main(String[] args) {
List<Number> dst = new ArrayList<Number>();
List<Integer> src = getSource();

copy2(dst, src); // Works when 2nd generic param type subclass of 1st
copy2(dst, dst); // Works when 2nd generic param type same as 1st
copy2(src, dst); // Invalid types for method!
}

Rather in the case of copy1, the first parameter is T, and the second parameter is still just the subclass of T. So we're talking about the same scenario here. In this context, there is no situation in which one would work and the other would not.

public static <T> void copy1(List<T> dst, List<? extends T> src) {
// Copy
}

public static void main(String[] args) {
List<Number> dst = new ArrayList<Number>();
List<Integer> src = getSource();

copy1(dst, src); // Works when 2nd generic param type subclass of 1st
copy1(dst, dst); // Works when 2nd generic param type same as 1st
copy1(src, dst); // Invalid types for method!
}

How do I clone a generic list in C#?

You can use an extension method.

static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}

How to clone ArrayList and also clone its contents?

You will need to iterate on the items, and clone them one by one, putting the clones in your result array as you go.

public static List<Dog> cloneList(List<Dog> list) {
List<Dog> clone = new ArrayList<Dog>(list.size());
for (Dog item : list) clone.add(item.clone());
return clone;
}

For that to work, obviously, you will have to get your Dog class to implement the Cloneable interface and override the clone() method.

Cloning with generics

Solution

Use the Java Deep-Cloning library.

The cloning library is a small, open source (apache licence) java library which deep-clones objects. The objects don't have to implement the Cloneable interface. Effectivelly, this library can clone ANY java objects. It can be used i.e. in cache implementations if you don't want the cached object to be modified or whenever you want to create a deep copy of objects.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Addendum

A previous answer had the following drawbacks:

  • It adds a lot of code
  • It requires you to list all fields to be copied and do this
  • This will not work for Lists when using clone(); the clone() for HashMap notes: Returns a shallow copy of this HashMap instance: the keys and values themselves are not cloned, so you end up doing it manually.

Serialization is also bad because it may require adding Serializable everywhere.

How to properly implement the call to clone() in a generic class [Java]

Because the clone method is marked as protected on the Object class, you cannot in general call this method on arbitrary objects. The idea behind the clone() method is that classes which supported it would override the method, declaring it as public.

The only real solution here that preserves full functionality is to use reflection to access the method and get around the access modifiers.

So here's my solution,

public class B<E extends Cloneable> {
E data;

public B(E elem) {
this.data = elem;
}

@SuppressWarnings("unchecked")
public E get() {
Method clone = null;
try {
clone = data.getClass().getMethod("clone");
Object[] args = new Object[0];
return (E) clone.invoke(data, args);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}

}
}

Clonable determines the behavior of Object’s protected clone implementation: if a class implements Cloneable, Object’s clone method returns a field-by-field copy of the object; otherwise it throws CloneNotSupportedException. But the way you have implemented the clone method in class A does not call Object's clone method so this has no effect.

@Override
public A clone() throws CloneNotSupportedException {
A temp = new A(this.value, this.name);
return temp;
}

If you want to make use of that facility you have to implement it like so,

public class A implements Cloneable {
int value;
String name;

public A(int x, String str) {
this.value = x;
this.name = str;
}

@Override
public A clone() throws CloneNotSupportedException {
return (A) super.clone();
}

public boolean equals(A elem) {
return (this.name).equals(elem.name) && this.value == elem.value;
}
}

In this case if your class A does not implement Cloneable then the java.lang.CloneNotSupportedException will be thrown.

Finally the declaration public class B<E extends Cloneable> gives you a compiler error if you try to pass in something that does not implement Cloneable to the B constructor in your Demo class.

B<A> c = new B<>(doesNotImplCloneable);   // Gives a compilation error.

So if you are using the Object's clone method as I have shown here the extends/implements Cloneable is the way to go.



Related Topics



Leave a reply



Submit