Java cloning method does not work

1.2k views Asked by At

I am copying the the current best solution of my program as follows:

public Object clone() 
{   
    MySolution copy = (MySolution)super.clone();
    copy.wtour =    (int[])this.wtour.clone();
    copy.w =        (int[][][][])this.w.clone();

    return copy;
}

When I request the overall best solution in the end, the program always gives me the current best solution but never the one with the best objective value. Also, nothing changes with the solution if I exclude the stated part from my program.

Edit: It is part of a tabu search optimizer that generates solutions and saves new current best solutions (as here) The clone method saves the tours of a routing problem where w[][][][] is a binary decision variable and wtour is a copy of that but consists of the customer numbers in the visiting sequence, i.e. [0, 5, 3, 2, 1, 4].

Edit: I changed my program according to Robby Cornelissen as follows:

    public Object clone()   {   
        MySolution copy = (MySolution)super.clone();
        copy.w = copy2(w);
        return copy;
    } 

    public static int[][][][] copy2(int[][][][] source) {
    int[][][][] target = source.clone();

    for (int i = 0; i < source.length; i++) {
        target[i] = source[i].clone();

        for (int j = 0; j < source[i].length; j++) {
            target[i][j] = source[i][j].clone();

            for (int q = 0; q < source[i][j][q].length; q++) {
                target[i][j][q] = source[i][j][q].clone();

            }
        }
    }

    return target;
}

As a result, i get a clone as follows:

w[0][5][1][0]=1
w[4][2][2][0]=1
w[2][5][3][0]=1
w[5][0][4][0]=1
w[0][4][1][1]=1
w[6][1][2][1]=1
w[1][3][3][1]=1
w[3][0][4][1]=1

The problem now is that only the first element of this belongs to the very best solution (w[0][5][1][0]). Why do the other ones not get copied?

SOLUTION: I changed my program as the provided link suggested to the following:

public Object clone()
{   
    MySolution copy = (MySolution)super.clone();
    copy.w = deepCopyOf(w);
    return copy;
}   // end clone

@SuppressWarnings("unchecked")
public static <T> T[] deepCopyOf(T[] array) {

    if (0 >= array.length) return array;

    return (T[]) deepCopyOf(
            array, 
            Array.newInstance(array[0].getClass(), array.length), 
            0);
}

private static Object deepCopyOf(Object array, Object copiedArray, int index) {

    if (index >= Array.getLength(array)) return copiedArray;

    Object element = Array.get(array, index);

    if (element.getClass().isArray()) {

        Array.set(copiedArray, index, deepCopyOf(
                element,
                Array.newInstance(
                        element.getClass().getComponentType(),
                        Array.getLength(element)),
                0));

    } else {

        Array.set(copiedArray, index, element);
    }

    return deepCopyOf(array, copiedArray, ++index);
}
1

There are 1 answers

6
Robby Cornelissen On BEST ANSWER

The problem is the following:

  • When you clone an array of primitives, the array object and its values are cloned.
  • When you clone an array of objects, the array object is cloned, but the cloned array will contain references to the same objects that were contained in the original array.

Now what does that mean for your case?

  • Your wtour value seems to be an array containing primitive ints, so the first of the two cases above applies. I.e. both the array and it's contents are effectively copied.
  • Your w value however seems to be a multidimensional array of ints. In practice this means that it's actually an array containing array objects, hence the second case applies. Although your top-level array object is copied, the copied array contains references to the same second-level array objects as the original array.

A similar issue and possible solutions are discussed here.

Update

As requested in the comments, a straightforward implementation could look like this. Note that this is completely untested:

public static int[][][] copy(int[][][] source) {
    int[][][] target = source.clone();

    for (int i = 0; i < source.length; i++) {
        target[i] = source[i].clone();

        for (int j = 0; j < source[i].length; j++) {
            target[i][j] = source[i][j].clone();
        }
    }

    return target;
}