0

I want to store several 2d array positions in an ArrayList and I've set it up like so:

ArrayList<Integer> playedValues = new ArrayList<Integer>();
playedValues.add(dataArray[0][1]);
playedValues.add(dataArray[0][2]); //example of adding 2d array positions

int size = playedValues.size(); //get size of ArrayList
int gridIndex = playedValues.get(size - 1); //trying to set gridIndex to dataArray[0][2]

cell.setCell(0, gridIndex); //this is where the error occurs

//trying to pass gridIndex to here
public void setCell(int value, int gridIndex) {
    gridIndex = value;
}

My issue is when I try and pass gridIndex as a parameter to setCell, I get a NullPointerException.

The method I'm passing it to seems to work fine in my tests, so I assume gridIndex is not being set properly. How do I make playedValues store dataArray[][] itself instead of the information dataArray[][] holds?

Matt C
  • 3,850
  • 5
  • 22
  • 42

2 Answers2

3

The solution you were looking for

After talking with you in chat, we determined that you were making a Sudoku game, and you wanted to store moves, and be able to undo those moves.

You have the board stored as sudokuGrid which is a 2d int array. You need to be able to make changes to certain cells (row, column). You also need to be able to store moves, (row, column), and in order. Then you can use both of these features together, and allow your users to undo their moves.

Let's focus on making changes to a cell first.

Undo'ing a move

We can write a method that takes in a row and a column, and changes the value of the cell at (row, column) back to 0. (Undo-ing a move).

We should be able to call this method like so:

int testRow = 4, testColumn = 1;
undoMove(testRow, testColumn, sudokuGrid);

Of course, later we'll use row's and column's from previous moves, but we need to be able to test right now. The undoMove method will be written like so:

public void undoMove(int row, int column, int[][] board) {
    board[row][column] = 0;
}

It's very important that we pass in sudokuGrid to undoMove. Otherwise, undoMove wouldn't be able to make permanent changes to the game board. You can read the link I gave farther below for more information on why you are able to make changes to sudokuGrid.

Now, we need to focus on how we can store moves.

Storing moves

We have a few options available, but the easiest in my opinion, would be to make an ArrayList and store Move's in it. Of course, Java hasn't defined Move's for us. We will have to make our own class to define a Move.

If you're unfamiliar with making classes, you can learn the basics here, and here.

Here's what our Move class might look like:

class Move {
    int row, column;

    public Move(int row, int column) {
        this.row = row;
        this.column = column;
    }
}

public void undoMove(int row, int column, int[][] board) {
    board[row][column] = 0;
}

This simply says that each Move will have a row, a column. We can now store the row and column of the cell where each move was played.

Now we can make an ArrayList to hold our Move's:

ArrayList<Move> moves = new ArrayList<Move>();

We can make a Move object each time that a user makes a move like so:

// You'll need to get the row and column 
// before you make the Move object
Move currentMove = new Move(row, column);

Now anytime we want to undo a move, we can simply do this:

Move moveToUndo = moves.get( moves.size() - 1 );
undoMove(moveToUndo.row, moveToUndo.column, sudokuGrid);
moves.remove( moves.size() - 1 );

Now all together!

class Move {
    int row, column;

    public Move(int row, int column) {
        this.row = row;
        this.column = column;
    }
}

public void undoMove(int row, int column, int[][] board) {
    board[row][column] = 0;
}

// Later on, in your game loop method
ArrayList<Move> moves = new ArrayList<Move>();

// Adding moves
// In your program, you'll get the row and column from what
// the user types in or clicks on. 
int row = 1, column = 7;
moves.add( new Move(row, column) );

row = 6, column = 8;
moves.add( new Move(row, column) );

// Undo'ing moves
Move moveToUndo = moves.get( moves.size() - 1 );
undoMove(moveToUndo.row, moveToUndo.column, sudokuGrid);
moves.remove( moves.size() - 1 );

You could even re-write the undoMove method to take in a Move, rather than a row and a column, and that would look very clean. You could also write a method undoLastMove that would automate some of this process.


Fixing your ArrayList problem

I'm not quite sure what you intend to use the ArrayList playedValues for, so I have to make a somewhat educated guess that you want to have an ArrayList hold arrays, not single values.

That is very simple to do!

Your current declaration:

ArrayList<Integer> playedValues = ArrayList<Integer>();

Sets up playedValues to hold Integer or int values. To instead set up playedValues to hold arrays of int's, do this:

ArrayList<Integer[]> playedValues = ArrayList<Integer[]>();

Then playedValues will store arrays of Integer's or int's.

You can use the ArrayList like so:

ArrayList<Integer[]> playedValues = ArrayList<Integer[]>();

int[] arrayOne = {1, 2, 3, 4, 5};

// Adding arrays to the list
playedValues.add(arrayOne);

// Iterating through all arrays in playedValues
for(int i = 0; i < playedValues.size(); i++) {
    int[] currentArray = playedValues.get(i);

    // Then, of course, you can loop over each array like normal:
    for(int j = 0; j < currentArray.length; j++) {
        System.out.println(
            "Array #" + i + " - Element #" + j + " = " + currentArray[i][j]
        );
    }
}

There's an even better way!

If you're using an ArrayList the same way as you would a 2d array, but you still want all of the flexibility and features of an ArrayList, then do this:

ArrayList<ArrayList<Integer>> playedValues = ArrayList<ArrayList<Integer>>();

That sets up playedValues to be an ArrayList which holds ArrayLists which hold int's. Very similar to using int[][] which uses an array that holds arrays that hold ints'.

The above code can be implemented with this new ArrayList of ArrayLists, like so:

ArrayList<ArrayList<Integer>> playedValues = ArrayList<ArrayList<Integer>>();

int[] arrayOne = {1, 2, 3, 4, 5};

// Adding values to the playedValues
playedValues.add( Arrays.asList(arrayOne) );

// Iterating through all values in playedValues
for(int i = 0; i < playedValues.size(); i++) {
    for(int j = 0; j < playedValues.get(i).size(); j++) {
        System.out.println(
            "Row #" + i + " - Column #" + j + " = " + playedValues.get(i).get(j)
        );
    }
}

If you wanted to store, maybe a 9x9 grid, you could easily do that like this:

ArrayList<ArrayList<Integer>> playedValues = ArrayList<ArrayList<Integer>>();
int[] sampleRow = {0, 0, 0, 0, 0, 0, 0, 0, 0};

for(int i = 0; i < 9; i++) {
    playedValues.add( Arrays.asList(sampleRow) );
}

But, should you use ArrayList?

I don't see a huge need for using an ArrayList here. When I see the code you have and read what you are trying to do, I immediately think 2d array.

There isn't anything in your code, or my answer, that can't be done with a 2d array. It may make things simpler too. The only issue, is if you need to dynamically add arrays. A 2d array can't do that.

Here is the same implementation as above, with a 2d array:

// 100 for rows and columns was chosen arbitrarily.
int[][] playedValues = new int[100][100];

int[] arrayOne = {1, 2, 3, 4, 5};
int[] arrayTwo = {1337, 9001, 1111111, 22222, 33333, 444, -1, -123, 246};

// Adding arrays to the list
playedValues[0] = arrayOne;
playedValues[1] = arrayTwo;

// Iterating through all arrays in playedValues
for(int i = 0; i < playedValues.length; i++) {
    for(int j = 0; j < playedValues[i].length; j++) {
        System.out.println(
            "Array #" + i + " - Element #" + j + " = " + currentArray[i][j]
        );
    }
}

And the setCell method:

public void setCell(int value, int row, int column, int[][] grid) {
    grid[row][column] = value;
}

Fixing the setCell method

If we have an ArrayList which stores arrays of int's, we can change a certain cell with this implementation:

// Making a call to set the cell in the 1st row at the 4th column, to 0
setCell(0, 1, 4, playedValues);

public void setCell(int value, int row, int column, ArrayList<Integer[]> grid) {
    int[] rowArray = grid.get(row);
    rowArray[column] = value;
}

Original Error

The problem isn't with gridIndex, it's with cell.

You can get a NullPointerException from an int, since all int's have default/null values of 0 in Java. See the documentation here, here, here, and this post's answers, for proof of this.

So the problem isn't with the setCell method, it's with your cell variable. The NullPointerException is being thrown because cell is null, not anything you pass to setCell.


Why setCell didn't originally work

It is true, as pointed out by other answers, that the method setCell won't actually do anything noticeable. This is because Java is pass by value, not pass by reference. You can read this post for a detailed explanation.

What this means behind the scenes, is that whatever actual number values you pass to setCell as value and gridIndex, setCell will have the values of, but not the references.

This code:

int gridIndex = 10, myValue = 2;
setCell(myValue, gridIndex);

will call setCell and pass the values of gridIndex and myValue, which are 10 and 2. The setCell method never sees the actual variables gridIndex and myValue, setCell has no reference to those variables, it only ever sees the values 10 and 2.


This can be confusing because the values are stored in variables within setCell, the variables you specify to be the parameter names (int value, int gridIndex), but these variables, are not the variables you passed to setCell. See this code:

public static void main(String[] args) {
    int gridIndex = 10, myValue = 9999;
    setCell(myValue, gridIndex);
    System.out.println(gridIndex);
}

public static void setCell(int value, int gridIndex) {
    // In here, value is NOT the same variable, myValue, that
    // we passed in. Just the same, gridIndex, is not the same 
    // variable that we passed in from main.

    // In here, value and gridIndex have the SAME VALUES
    // as the variables we pass in, but they are not the same
    // variables. They are newly made variables, with the same values.
}

Also look at this code:

public static void main(String[] args) {
    setCell(10, 9999);
    System.out.println(gridIndex);
}

public static void setCell(int value, int gridIndex) {
    gridIndex = value;
}

We didn't pass a variable to setCell, but inside setCell we changed the variable gridIndex to be equal to value. Will this tell Java that the new value of the number 9999 should really be 10? Of course not. We aren't changing what was passed in, we are changing variables that hold the values that were passed in.


An easy way to think about what is happening, is to remember that whenever you use an int variable in Java, you could replace the variable name with the value it holds, and the code would remain exactly the same.

Take this code for example:

int gridIndex = 10, myValue = 9999;
setCell(myValue, gridIndex);

an equilivant code is:

setCell(10, 9999);

And as we just saw above, of course we can't change the value of 10 or 9999. We can't just decide that numbers are equal other numbers. Of course we can change the numbers that are held in int's, but there aren't any int variables being delivered to setCell, only number values like 10 or 9999.

Matt C
  • 3,850
  • 5
  • 22
  • 42
  • Thanks, I fixed the issue with cell here, but I am still having issue with the ArrayList not holding the gridIndex position, only the value it points to. – snazzyjazzy Apr 16 '16 at 07:46
  • @snazzyjazzy Read my new edit, and wait just a minute for another edit to arrive. – Matt C Apr 16 '16 at 07:47
  • @snazzyjazzy Did my answer help? – Matt C Apr 16 '16 at 08:14
  • It helped with my understanding, and I fixed the issue with cell, however my overall issue of not being able to use ArrayList to store my gridIndex[][] still exits. – snazzyjazzy Apr 16 '16 at 08:16
  • Ok, I'll add that in. – Matt C Apr 16 '16 at 08:16
  • @snazzyjazzy I added a section of code that will fix that problem. – Matt C Apr 16 '16 at 08:21
  • @snazzyjazzy I also just added a section that talks about whether or not you should use a 2d array, or an arraylist. Be sure to read the `But, should you use ArrayList?` section. I'm not entirely sure what your "grid" or ArrayList is attempting to accomplish, but from what I'm guessing it tries to do, you could just as well use a 2d array. Could you explain what you are trying to accomplish? If you can, I'll be able to better advise you on what route to take. – Matt C Apr 16 '16 at 08:31
  • I'm using a 2d array (gridIndex[row][column]) to store values placed on a sudoku board. I need to be able to undo a move, so I'm trying to store played numbers' 2d array position in an arraylist so I can 'undo' the last move (set the 2darray to 0). – snazzyjazzy Apr 16 '16 at 08:36
  • @snazzyjazzy Oh I see. Well in that case, the solution is very easy. But it does change depeding on if you want to store only the very last move, or all moves? – Matt C Apr 16 '16 at 08:44
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109307/discussion-between-snazzyjazzy-and-matthew-cliatt). – snazzyjazzy Apr 16 '16 at 08:46
  • @snazzyjazzy I have taken everything you said, including from chat (as of 5:21am EST) and compiled a solution at the top of my answer. – Matt C Apr 16 '16 at 09:27
  • 1
    Thank you so much for being so thorough and patient! Your solution is exactly what I was looking for! – snazzyjazzy Apr 17 '16 at 04:32
  • 1
    @snazzyjazzy I'm glad I could help! :) I do have a quick and friendly suggestion though - be careful not to fall for the XY problem! The XY problem is when your original problem becomes unclear because you are trying to solve a workaround for the problem, not the problem itself. It happens to the best of us, even very recently [to myself](http://www.stackoverflow.com/q/36557004)! You can everything about this XY problem [here](http://mywiki.wooledge.org/XyProblem) and learn how to avoid it [here](http://meta.stackexchange.com/q/66377). – Matt C Apr 17 '16 at 04:50
-1
public void setCell(int value, int gridIndex) {
  gridIndex = value;
}

The above code does nothing. It takes two arguments, and locally sets one of the arguments to the other. Then since both of the variables were declared within the function, they are both gone when it exits.

Since this method just sets gridIndex, why not just set it directly?

gridIndex = 0;

EDIT: example of why this doesn't work.

I tried this in my IDE - it is exactly the same setCell method you have.

public static void main(String[] args) {
    int gridIndex = 10;
    setCell(0, gridIndex);
    System.out.println(gridIndex);
}

public static void setCell(int value, int gridIndex) {
    gridIndex = value;
}

The console prints 10, because setCell does nothing. It doesn't set the value of gridIndex, it doesn't edit any variables. It does nothing, because the only values it changes are values which are declared in its signature, and which are lost when the method exits as a result.

nhouser9
  • 6,572
  • 3
  • 18
  • 39
  • I pass in a gridIndex position (eg dataArray[0][0]) and assign it to a value. Therefore I change the value of dataArray. I have tested setCell in junit and it works the way I expected it to. – snazzyjazzy Apr 16 '16 at 07:17
  • 1
    @snazzyjazzy you declare a local variable named gridIndex in the method signature. Then you set it to something. Then gridIndex is lost when the method exits. Tested it in my IDE and verified that it doesn't do anything. – nhouser9 Apr 16 '16 at 07:20
  • 1
    @snazzyjazzy edited my answer with an example showing that the method does nothing. – nhouser9 Apr 16 '16 at 07:24
  • @nhouser9 You're completely right about the method not doing anything, but making it do absolutely anything won't fix the error the poster receives. – Matt C Apr 16 '16 at 09:17