Search code examples
javaarraysmatrixclone

Cloning an object and its inside arrays in Java DIY


I am trying to clone an object without using any library. The object has other objects/arrays/matrixes in it. So I developed some methods to clone those as well.

They are working fine when I am cloning arrays/matrixes that are not inside the object. These are the methods:

public static int[] cloneArray(int[] array){
    int i = 0;
    int[] clone = new int[array.length];
    while (i < array.length){
        clone[i] = array[i];
        i++;
    }
    return clone;
}

public static int[][] cloneMatrix(int[][] matrix){
    int[][] clone = new int[matrix.length][matrix[0].length];
    for (int i = 0;i<matrix.length;i++)
        for(int j = 0;j<matrix[0].length;j++)
            clone[i][j] = matrix[i][j];
    return clone;
}

However, when I want to clone an object, the references of the array/matrix stay the same, as you can check in the output on the bottom of the post. This is the constructor I have:

    public State(int parentStateID, int stateID, int[][] board, int[] pieces, int points, int acquiredPoints){
       State.parentStateID = parentStateID;
       State.stateID = stateID;
       State.currentBoard = cloneMatrix(board); //here takes place the matrix cloning
       State.currentPieces = cloneArray(pieces); //here takes place the array cloning
       State.totalPoints = points;
       State.acquiredPoints = acquiredPoints;
   }

And this is the cloning method:

    public static State cloneState(State state){
    int[][] currentBoard = state.getCurrentBoard();
    int[] currentPieces = state.getCurrentPieces();
    int totalPoints = state.getTotalPoints();
    int acquiredPoints = state.getAcquiredPoints();
    int parentStateID = state.getParentStateID();
    int stateID = state.getStateID();
    State clone = new State(parentStateID, 
            stateID,
            currentBoard, 
            currentPieces, 
            totalPoints, 
            acquiredPoints);
    return clone;
}

To better visualize the output, here are the arrays and matrix:

public static int piecesList[] = {1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
public static int piecesList2[] = {2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
public static int piecesList3[] = {3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
private static int[][] map = {{0,1,2,3,4},{5,6,7,8,9},{10,11,12,13,14},{15,16,17,18,19},{20,21,22,23,24}};

Here is the verification code:

    int[][] state2 = cloneMatrix(map);
    state2[0][0] = 1;
    System.out.println("map.original " + map[0][0]);
    System.out.println("map.clone " + state2[0][0]);

    System.out.println("");
    System.out.println("");

    int[] pc = cloneArray(piecesList);
    pc[24] = 1;
    System.out.println("pieces.original " + piecesList[24]);
    System.out.println("pieces.clone " + pc[24]);

    System.out.println("");
    System.out.println("");
    State newState = setFirstState();
    State clonedState = cloneState(newState);
    clonedState.setCurrentPieces(piecesList2);
    System.out.println("newState.pieceslist: "+newState.getCurrentPieces()[0]);
    System.out.println("clonedState.pieceslist: "+clonedState.getCurrentPieces()[0]);
    System.out.println("piecesList.original: "+piecesList[0]);
    System.out.println("");
    newState.setCurrentPieces(piecesList3);
    System.out.println("newState.pieceslist: "+newState.getCurrentPieces()[0]);
    System.out.println("clonedState.pieceslist: "+clonedState.getCurrentPieces()[0]);
    System.out.println("piecesList.original: "+piecesList[0]);

And here is the output:

map.original 0
map.clone 1


pieces.original -1
pieces.clone 1

//as you can check in the next two cases, the change takes effect in both the original state, and the cloned state
newState.pieceslist: 2
clonedState.pieceslist: 2
piecesList.array variable: 1

newState.pieceslist: 3
clonedState.pieceslist: 3
piecesList.array variable: 1

Been trying to solve this issue for over 12 hours, without much success... I have tried libraries as well as serialization with no success... Help is much appreciated!


Solution

  • The problem with your code is that the fields (not shown) obviously are static, as can be told from your constructor:

    public State(int parentStateID, int stateID, int[][] board, int[] pieces, int points, int acquiredPoints){
        State.parentStateID = parentStateID;
        State.stateID = stateID;
        State.currentBoard = cloneMatrix(board); //here takes place the matrix cloning
        State.currentPieces = cloneArray(pieces); //here takes place the array cloning
        State.totalPoints = points;
        State.acquiredPoints = acquiredPoints;
    }
    

    The constructor should instead read like this:

    public State(int parentStateID, int stateID, int[][] board, int[] pieces, int points, int acquiredPoints){
        this.parentStateID = parentStateID;
        this.stateID = stateID;
        this.currentBoard = cloneMatrix(board); //here takes place the matrix cloning
        this.currentPieces = cloneArray(pieces); //here takes place the array cloning
        this.totalPoints = points;
        this.acquiredPoints = acquiredPoints;
    }
    

    A field which is static exists only once per class. A field which is not static exists once per each object constructed from that class (or any subclass). Because of that, you actually created objects which are empty, and whatever you thought you would store in the object you actually stored in the class, thus sharing the same data among all objects.

    As a rule of thumb, you usually do not want any static non-final fields in your classes. Exceptions apply, but they're rare and mostly limited to a few tiny bootstrapping things in frameworks.