2

I have a class with these private members:

private ArrayList<ArrayList<ArrayList<CustomStuff>>> listOfPaths;
private int currentIndex;

I fill the array in a method like this:

listOfPaths.get(currentIndex).add(path); //path is ArrayList<CustomStuff>

All is fine so far.
Checking:

System.out.println(listOfPaths.get(currentIdx).get(listOfPaths.get(currentIdx).size() - 1).size());

Gives the right size.

Now: After the method finishes. There is only one object left in every single leaf in the ArrayList<ArrayList<ArrayList<CustomStuff>>>

So System.out.println(listOfPaths.get(anyValidIdx).get(anyValidIdx).size()); will always be 1!

By the way: listOfPaths.size() and listOfPaths.get(anyValidIdx).size() give the right sizes!

So only the third dimension of the array seem to shrink to a single object.

What is going wrong?

Background Story: I have points on a matrix. Start and end marks. Between those marks I have paths. A path consists of steps. so:
- A path is ArrayList<Step>.
- Collection of different paths for the same start/end marks is: ArrayList<ArrayList<Step>>.
- All collections on the matrix is ArrayList<ArrayList<ArrayList<Step>>>.

I start with a pair of marks, and look for all the available paths. When searching I add every found path: listPaths.get(currentIndex).add(pathBetweenStartAndEnd)
So when I am finished fetching paths for a pair of marks, I increment the currentIndex and move to the next pair of marks and so on...

Complete code:

import java.util.ArrayList;

public class Solver {

    protected GameBoard board;

    private static ArrayList<ArrayList<ArrayList<BoardCell>>> listOfPaths;
    private int currentPair;

    public GameBoard getSolvedBoard() {
        solve();
        return board;
    }

    public void setBoard(GameBoard board) {
        this.board = board;
    }

    public Solver(GameBoard board)
    {
        super();

        this.board = board;
    }


    protected void solve()
    {
        listOfPaths = new ArrayList<ArrayList<ArrayList<BoardCell>>>();
        currentPair = 0;

        for(CellPair pair : board.getPairs())
        {
            System.out.printf("Getting paths for %d:\n", pair.getFirstCell().getValue());

            ArrayList<BoardCell> path = new ArrayList<BoardCell>();
            path.add(pair.getFirstCell());

            listOfPaths.add(new ArrayList<ArrayList<BoardCell>>());

            DFS(pair.getFirstCell(), pair.getSecondCell(), new ArrayList<BoardCell>(), path);

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

            ++currentPair;
        }

        System.out.println(listOfPaths.get(0).get(0).size());
        //System.out.println(listOfPaths.get(2).get(205).get(1));
    }

    protected static ArrayList<BoardCell> getSonsForCellOnBoard(BoardCell cell, GameBoard board)
    {
        int row     = cell.getRow(),
            column  = cell.getColumn();

        ArrayList<BoardCell> neighbors = new ArrayList<BoardCell>();

        if(row > 0)
            neighbors.add(board.getCellAtIndex(row - 1, column));
        if(row < board.getNumberOfRows() - 1)
            neighbors.add(board.getCellAtIndex(row + 1, column));
        if(column > 0)
            neighbors.add(board.getCellAtIndex(row, column - 1));
        if(column < board.getNumberOfColumns() - 1)
            neighbors.add(board.getCellAtIndex(row, column + 1));

        return neighbors;
    }

    private void DFS(   BoardCell source, 
                        BoardCell target, 
                        ArrayList<BoardCell> visited, 
                        ArrayList<BoardCell> path   )
    {
        if(source.getRow() == target.getRow() && source.getColumn() == target.getColumn())
        {
            System.out.printf("PATH: %d: ", path.size());
            System.out.println(path);

            ArrayList<BoardCell> temp = new ArrayList<BoardCell>();
            temp = path;

            listOfPaths.get(currentPair).add(temp);

            System.out.println(listOfPaths.get(currentPair).get(listOfPaths.get(currentPair).size() - 1).size());

            return; 
        }

        for(BoardCell son : Solver.getSonsForCellOnBoard(source, board))
        {
            if(visited.contains(son))
                continue;

            if(son != target &&
                    son.getType() == BoardCell.BoardCellType.BoardCell_AnchorCell)
                continue;

            path.add(son);
            visited.add(son);

            DFS(son, target, visited, path);

            visited.remove(son);
            path.remove(path.size() - 1);
        }
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Hasib Samad
  • 1,081
  • 1
  • 20
  • 39
  • Why do you have an `ArrayList` of an `ArrayList` of an `ArrayList`? There's got to be a better data structure. – Jared Nielsen May 11 '13 at 19:19
  • 1
    `ArrayList>>` If you see code like this you really need to rethink your structure... that much nested generics isn't going to help debugging – tckmn May 11 '13 at 19:19
  • As I said. I am new to java. If you know a better collection, please let me know. The thing is, this does not have to be generic. It always has to hold 3 dims. Anyway I would be still interested in knowing what is going wrong in my situation. – Hasib Samad May 11 '13 at 19:22
  • did you verify you have more than one path for a pair of marks ? – giorashc May 11 '13 at 19:46
  • You are talking about the wrong dimension. I still have multiple paths, it is that I have only ONE STEP left in each path. But to answer your question anyway: It is verified that there are multiple paths with multiple steps in them. – Hasib Samad May 11 '13 at 19:48
  • Can you post the complete code? I want to see the values you are adding to three dim list. – dejavu May 11 '13 at 19:49
  • @user1423640 You call this `ArrayList>>` a `listOfPaths`. This makes me think that an `ArrayList>` is a "path" (whatever that means in the context of your program). Make a class denoting that, to hide away the complexity. If you simplify your collections so that they don't *appear* nested (even if they are), most likely the problem will go away painlessly. – Theodoros Chatzigiannakis May 11 '13 at 20:07

1 Answers1

4

In Java non-primitive types (A list in your case stored in path) are passed by reference rather than by value.

When you call :

DFS(son, target, visited, path);

eventually at the end of the recursion you store path in your listOfPaths.

BUT right after you do :

visited.remove(son);
path.remove(path.size() - 1);

Since path was passed as a reference any change to it will affect the one stored in your listOfPaths.

So replace this (temp is redundant in this case btw):

ArrayList<BoardCell> temp = new ArrayList<BoardCell>();
temp = path;

listOfPaths.get(currentPair).add(temp);

With this (just copying the path list):

ArrayList<BoardCell> temp = new ArrayList<BoardCell>(); 
for (BoardCell bc : path)   
     temp.add(bc);

listOfPaths.get(currentPair).add(temp);

And look for more places in your code with this similar issue.

giorashc
  • 13,691
  • 3
  • 35
  • 71
  • Brilliant. It works. Thank you very much! Helped me on my journey to learn Java! – Hasib Samad May 11 '13 at 20:12
  • This is not 100% correct. [Objects are not passed by reference although it seems like they are](http://stackoverflow.com/questions/40480/is-java-pass-by-reference) – nkr May 11 '13 at 20:14
  • @nkr under the hood they are pointers and in Java mostly you learn to use them as references (of course depends how you interpret the word reference) since you cannot do *pointer in Java. You probably come from the c++ world :) as mentioned in one of the comments – giorashc May 11 '13 at 20:18
  • @giorashc: You are right but if a beginner with this knowledge wants to assign a new value to a non-primitive parameter he will be surprised that this will not work as he expected. – nkr May 11 '13 at 20:24