More efficient way to check neighbours in a two-dimensional array in Java
Hey all, for a few of开发者_Go百科 my college assignments I've found the need to check neighbouring cells in 2-dimensional arrays (grids). The solution I've used is a bit of a hack using exceptions, and I'm looking for a way to clean it up without having loads of if
statements like some of my classmates. My current solution is
for ( int row = 0; row < grid.length; row++ ) {
for ( int col = 0; col < grid.length; col++ ) {
// this section will usually be in a function
// checks neighbours of the current "cell"
try {
for ( int rowMod = -1; rowMod <= 1; rowMod++ ) {
for ( int colMod = -1; colMod <= 1; colMod++ ) {
if ( someVar == grid[row+rowMod][col+colMod] ) {
// do something
}
}
}
} catch ( ArrayIndexOutOfBoundsException e ) {
// do nothing, continue
}
// end checking neighbours
}
}
I shudder to think of the inefficiency using exceptions to get my code to work causes, so I'm looking for suggestions as to how I could remove the reliance on exceptions from my code without sacrificing readability if it's possible, and just how I could make this code segment generally more efficient. Thanks in advance.
You can try this. First decide the size of the grid Lets say its 8 X 8 & assign MIN_X = 0, MIN_Y = 0, MAX_X =7, MAX_Y =7
Your curren position is represented by thisPosX , thisPosY, then try this:
int startPosX = (thisPosX - 1 < MIN_X) ? thisPosX : thisPosX-1;
int startPosY = (thisPosY - 1 < MIN_Y) ? thisPosY : thisPosY-1;
int endPosX = (thisPosX + 1 > MAX_X) ? thisPosX : thisPosX+1;
int endPosY = (thisPosY + 1 > MAX_Y) ? thisPosY : thisPosY+1;
// See how many are alive
for (int rowNum=startPosX; rowNum<=endPosX; rowNum++) {
for (int colNum=startPosY; colNum<=endPosY; colNum++) {
// All the neighbors will be grid[rowNum][colNum]
}
}
you can finish it in 2 loops.
So row
and col
currently contain the coordinate of the cell that I want to check the neighbours of. So if I have a class variable called START_OF_GRID
which contains 0
, my solution would be as follows:
int rowStart = Math.max( row - 1, START_OF_GRID );
int rowFinish = Math.min( row + 1, grid.length - 1 );
int colStart = Math.max( col - 1, START_OF_GRID );
int colFinish = Math.min( col + 1, grid.length - 1 );
for ( int curRow = rowStart; curRow <= rowFinish; curRow++ ) {
for ( int curCol = colStart; curCol <= colFinish; curCol++ ) {
// do something
}
}
why can't you check row+rowMod and col+colMod for validity before array access?
something like:
r=row+rowMod;
c=col+colMod;
if (r < 0 || c < 0 || r >= grid.length || c >= grid.length) continue;
alternatively (no continue):
if (r >= 0 && c >= 0 && r < grid.length && c < grid.length &&
someVar == grid[r][c]) { /* do something */ }
The basic principle is not to access things that are out of bounds -- so either protect the bounds or don't go out of bounds in the first place. That is, start at a place where you won't immediately go out of bounds and stop before you get out of bounds.
for ( int row = 1; row < grid.length - 1; row++ ) {
for ( int col = 1; col < grid.length - 1; col++ ) {
// this section will usually be in a function
// checks neighbours of the current "cell"
for ( int rowMod = -1; rowMod <= 1; rowMod++ ) {
for ( int colMod = -1; colMod <= 1; colMod++ ) {
if ( someVar == grid[row+rowMod][col+colMod] ) {
// do something
}
}
}
// end checking neighbours
}
}
Like your current code, this doesn't necessarily deal appropriately with edge conditions -- that is, it applies a 3x3 grid everywhere that the 3x3 grid fits within the matrix, but does not shrink the grid to a 2x2, 2x3 or 3x2 grid when on the edge of the matrix. It will, however, allow a method in the main body checking a 3x3 grid to observe every cell in the matrix.
If I understand your code correctly, and am correctly guessing your concerns, you're trying to avoid checking a non-existent neighbour when the cell of interest is on one edge of the grid. One approach, which may or may not suit your application, is to put a 1-cell wide border all the way round your grid. You then run your loops across the interior of this expanded grid, and all the cells you check have 4 neighbours (or 8 if you count the diagonally neighbouring cells).
How about this:
private static void printNeighbours(int row, int col, int[][] Data, int rowLen, int colLen)
{
for(int nextR=row-1; nextR<=row+1; nextR++)
{
if(nextR<0 || nextR>=rowLen)
continue; //row out of bound
for(int nextC=col-1; nextC<=col+1; nextC++)
{
if(nextC<0 || nextC>=colLen)
continue; //col out of bound
if(nextR==row && nextC==col)
continue; //current cell
System.out.println(Data[nextR][nextC]);
}
}
}
private void fun(char[][] mat, int i, int j){
int[] ith = { 0, 1, 1, -1, 0, -1 ,-1, 1};
int[] jth = { 1, 0, 1, 0, -1, -1 ,1,-1};
// All neighbours of cell
for (int k = 0; k < 8; k++) {
if (isValid(i + ith[k], j + jth[k], mat.length)) {
//do something here
}
}
}
private boolean isValid(int i, int j, int l) {
if (i < 0 || j < 0 || i >= l || j >= l)
return false;
return true;
}
精彩评论