mtprogrammer
mtprogrammer

Reputation: 97

copying an array of objects and then modifying the original without affecting the copy

So I have been banging my head over this seemingly insignificant issue. I don't necessarily know what to search for. I have scoured around for solutions. I need to make a copy of 2D array. The array consists of objects (a class I created call Cell), but the as soon as I make a copy I store that copy into a hash map (for possible reference later), then continue to modify the original array. The issue is that the modifications to the original also affect the copy now in the hash map. Essentially at the end of the day, my hash map will consist of many versions of the same grid. I have tried array.clone(), System.arraycopy(...), Arrays.copyof(), the traditional for loop copying scheme.... Finally I realized that I need what was called a deep copy, where you copy each data field of each object into a new object into the array copy....yeah, that didn't work either. Take a look:

static Cell[][] gridCopy;
...
Cell[][] grid = getGrid(file); //get grid from a file (this is Sudoku if you must know)
...
static boolean SolveSudoku(Cell grid[][])
{
// If there is no unassigned location, we are done
    if (unassigned == null)
        return true; // success!

    int row = unassigned.row;
    int col = unassigned.col;
    ArrayList<Integer> domain = unassigned.domain;

    // consider digits 1 to 9
    for (int num = 0; num < domain.size(); num++)
    {
        //if looks promising
        if (isSafe(grid, row, col, domain.get(num)))
        {
            //gridCopy = new Cell[N][N];
            instance++;
            // make tentative assignment
            grid[row][col].value = domain.get(num);

            //here is my attempt at a deep copy
            for (int i = 0; i < N; i++)
                for (int j = 0; j < N; j++)
                    gridCopy[i][j] = new Cell(grid[i][j].row, grid[i][j].col, grid[i][j].value, grid[i][j].domain);
            states.put(instance, gridCopy); //save the current state in a map for reference if we backtrack

            //as soon as I change things here in the original, the copy in the 'states' map also changes
            updateSpecifiedDomains(grid, row, col, domain.get(num), true);

            printGrid(grid, "Instance" + String.valueOf(instance));

            // return, if success, yay!
            if (SolveSudoku(grid, choice))
                return true;

            // failure, un-assign & try again
            //int temp = grid[row][col].value;
            grid = states.get(instance); //retain previous state
            grid[row][col].value = UNASSIGNED;

            /*updateSpecifiedDomains(grid, row, col, temp, false);
            while (domain.contains(temp))
                grid[row][col].domain.remove((Integer)temp);*/

            //domain.remove((Integer)num);
        }
    }
    count++;
    instance--;
    return false; // this triggers backtracking
}

Upvotes: 2

Views: 3541

Answers (3)

Rodrigo Quesada
Rodrigo Quesada

Reputation: 1470

If you are talking just about modifying the original array, then Arrays.copyof(...) should suffice because it creates a new array (not the same indeed), but I'm guessing that when you refer to "modifying the original", you are referring to modifying the objects referenced in it (not the array per se).

So you first need to implement a copy constructor or a copy method for each of the objects and then iterate the array and copy the objects into a new array.

Using Guava, you can easily do it like this (in this example that would be after implementing the clone method):

YourClass [] originalArray = new YourClass[]{...};
YourClass [] arrayCopy = Collections2.transform(Arrays.asList(originalArray), new Function<YourClass, YourClass>() {
    @Nullable
    @Override
    public Object apply(YourClass anObject) {
        return anObject.clone();
    }
}).toArray();

Or if you don't want the unnecessary overhead of having to convert to a Collection and then back to an array, you can use this small code I implemented (which also use Guava, but just the Function class): Transform a Generic Array Efficiently Using Guava

Finally I must say that you can implement a copy constructor or a copy method (or clone, or whatever you may want to call it) differently depending on your specific needs: Deep copy, shallow copy, clone

Upvotes: 0

Stav Saad
Stav Saad

Reputation: 599

Everything in java is a pointer. This means that copying an array they way you are implementing it creata indeed a pointer to a new array, but the elements in the arrays point to the elements in the old array, thus making modifying them via methods affect both arrays. In order for that not to happen, you must implement a deep-cloning mechanism to clone the elements of the original array, and insert the newly created clones into the new array. Modifying a clone will not affect the original element, because they point to different addresses in the heap.

Upvotes: 0

brso05
brso05

Reputation: 13222

You are creating a shallow copy of your objects. Array.clone() will only work on primitive types. You should create a method in the Class of the objects you are trying to copy that creates and returns a new instance of the class with the same attribute values. Then you can loop through your array getting a copy of each object and add them to a new array then store the new array to your hashmap.

Example:

public class MyClass()
{
    private String temp;

    public MyClass(String temp)
    {
        this.temp = temp;
    }

    public MyClass copy()
    {
       MyClass copy = new MyClass(this.temp);
       //set attributes in constructor or using setters so they are the same as this object
       return copy;
    }
}

Upvotes: 2

Related Questions