Home > Software engineering >  How to modify array without its modification in previously added deque?
How to modify array without its modification in previously added deque?

Time:07-07

I faced the next problem. The simplified view of my code is:

class Board{
Cell[][] cells = new Cell[9][9]
Deque<Cell[][]> cellsReserved = new ArrayDeque<>();
   public static void main(){
      cells[5][4] = new Cell(5);
      cellsReserved.add(cells);
      cells[5][4].setValue(10);
      cellsReserved.add(cells);
      while(!cellsReserved.isEmpty()){
         System.out.println(cellsReserved.poll()[5][4].getValue());
      }
   }
}

class Cell{
   private int value;
   public Cell(int value){
      this.value = value;
   }
   public void setValue(int value){
      this.value = value;
   }
   public int getValue() {
        return value;
    }
}

I want to get next result:

5
10

But the result is

10
10

I researched this issue and found that the core problem is that collections in Java save not values but references so modifying objects after setting its to collections causes their modification in collections. I found the solution for adding lists to deque:

deque.addLast(list.stream().toList())

or

deque.addLast(new ArrayList<>(list));

But such approach doesn't work with arrays because

cellsReserved.add(Arrays.stream(cells).toArray(Cell[][]::new))

doesn't solve problem and value in deque is modified after modification of value of Cell not in deque.

I would be grateful if you help me with this problem.

CodePudding user response:

There is only a single instance of Cell[][] cells here, so no matter how many times it is added to the Deque it is still the same reference to same instance. Moreover cells[5][4].setValue(10); overrides the value inside the selected Cell instance, so you need to create a new Cell each time.

It is not very clear from the question what is the overall purpose of this code. If the goal is to have a "history" per board cell, consider using an array of Deques instead. Since arrays of generics are not directly supported (possible with Array.newInstance() but won't get into this now), the array-of-arrays can be replaced by a flat ArrayList, but you will have to calculate the coordinates explicitly. It can be something like this:

List<Deque<Cell>> cellsReserved = new ArrayList<>(9 * 9);
for (int i = 0; i < 9 * 9; i  ) {
    cellsReserved.add(new ArrayDeque<>());
}
Deque<Cell> dequeOf5_4 = cellsReserved.get(5   4 * 9);
dequeOf5_4.add(new Cell(5));
dequeOf5_4.add(new Cell(10));

while(!dequeOf5_4.isEmpty()){
    System.out.println(dequeOf5_4.poll().getValue());
}

Update following @Ensei's comment: For the case you need snapshot of the entire array/matrix in each Deque element, you can deep copy the matrix before each change. This may look like this:

    public static void main(String[] args){
        Deque<Cell[][]> cellsReserved = new ArrayDeque<>();
        Cell[][] cells = new Cell[9][9];
        cells[5][4] = new Cell(5);
        cellsReserved.add(cells);

        cells = deepCopy(cells);
        cells[5][4].setValue(10);
        cellsReserved.add(cells);

        while(!cellsReserved.isEmpty()){
            System.out.println(cellsReserved.poll()[5][4].getValue());
        }
    }

    private static Cell[][] deepCopy(Cell[][] cells) {
        Cell[][] copy = new Cell[cells.length][cells[0].length];
        for (int i = 0; i < cells.length; i  ) {
            for (int j = 0; j < cells[i].length; j  ) {
                Cell source = cells[i][j];
                if (source != null) {
                    copy[i][j] = new Cell(source.getValue());
                }
            }
        }
        return copy;
    }

Note that the deep copy method creates a new instance of the array and a new instance of each Cell object.

  • Related