Я создаю пошаговую игру. В этой игре у меня есть сетка размером N x N.
Я подумал, что было бы неплохо использовать 2D-логический массив для представления бинарных состояний всех ячеек в этой сетке.
Представлять себе:
// T = true | F = false
---------------------
| F | | | | |
---------------------
| | T | | | |
--------------------- // i.e. grid[0][0] = false
| | | | | |
---------------------
| | | | | |
---------------------
Каждый квадрат может быть либо истинным, либо ложным. Правила игры не важны... Просто учтите, что каждая ячейка может быть истинной или ложной.
Это то, что я пытался реализовать:
public class Life {
Boolean[][] grid;
public Life(int x, int y, Boolean status) {
if (!this.grid[x][y]) {
this.grid = new Boolean[x][y];
}
this.grid[x][y] = status;
}
}
Который я бы создал следующим образом:
new Life(0,0,false);
new Life(2,1,true);
Однако, когда я делаю это, моя программа падает, и я не уверен, что я делаю неправильно. Любая помощь приветствуется.
Caused by: java.lang.NullPointerException at Life.(Life.java:6)
@DominikWosiński Понятно, но как бы я, например. установить grid[0][0] = true
?
@Joel: используйте примитив boolean
, который по умолчанию является false
. Boolean
является объектом и находится null
в вашей сетке
@Cratylus, к сожалению, я получаю ту же ошибку.
@Joel: вы не инициализировали массив. Проверить ответ Павла Смирнова
@Joel: Также grid
должно быть static
когда вы говорите if (!this.grid[x][y])
, вы пытаетесь получить доступ к элементу [x][y], но массив еще не инициализирован. Вместо этого это должно быть просто if (this.grid != null)
и Кратил, вероятно, прав. Если вы хотите, чтобы каждый объект Life имел одну и ту же сетку, то он должен быть помечен static
. это, кажется, ваше намерение.
Я думаю, что здесь все пытаются изобрести велосипед, когда Life должен быть очень простым объектным классом с методами setStatus и getStatus для доступа к элементам в массиве. Мы не должны устанавливать какие-либо статусы в конструкторе.
Вы обращаетесь к полю grid
в конструкторе, экземпляр которого еще не создан.
Что вы, наверное, хотите сделать, это:
public class Life {
boolean[][] grid;
public Life(int x, int y, boolean status) {
grid = new boolean[5][5]; //where 5 is the size of the matrix: 5x5
grid[x][y] = status;
}
}
Примечание. Я использую примитив boolean
вместо объекта Boolean
, так как создание объекта в вашем случае излишне.
Обновлено:
Если Life
предполагается только одна ячейка, то было бы лучше объявить поле grid
статическим и создать его экземпляр только один раз. Любые действия с ячейками можно производить через геттеры\сеттеры:
public class Life {
static boolean[][] grid = new boolean[5][5]; //where 5 is the size of the matrix: 5x5;
public void setStatus(int x, int y, boolean status) {
grid[x][y] = status;
}
public boolean getStatus(int x, int y) {
return grid[x][y];
}
}
Я заметил, что вы также удалили this.
. В любом случае. Интересно, но я действительно не понимаю Зачем, мне нужно создать экземпляр моей сетки с размером сетки... хм. Думал, что смогу просто создать экземпляр одной ячейки за раз.
@ Джоэл, прежде чем вы сможете использовать какую-либо ячейку в массиве 2d, вы должны сначала создать экземпляр всего массива, выделить память для всех значений. Если вы хотите создать только одну ячейку за раз и получить к ней доступ с помощью составного ключа x
и y
, вам, вероятно, следует взглянуть на другую структуру данных, например, HashMap
, где x
и y
— ключи, а Boolean
— значение. .
@Joel, вы можете установить размер массива, используя параметры x и y, а не целочисленные литералы в этом ответе.
Угу... не уверен, что мне нравится, как с этим справляется Java. Злоупотребление памятью .. Было бы намного лучше, если бы я мог выделить место в любом месте массива, как мне заблагорассудится. В любом случае, спасибо за ответ. Одобренный.
@brandonx, я полагаю, x
и y
- это просто координаты для установки логического значения, а не размера всего массива.
@ Джоэл, пожалуйста :) Вы также можете рассмотреть возможность использования List<List<Boolean>>. Это почти то же самое, но хм... это изменяемый массив логических значений. При таком подходе вы можете получить доступ к значениям с помощью list.get(x).get(y);
Но это намного больше потребляет памяти.
@PavelSmirnov ваше предположение верно. Вот почему мне трудно это переварить. Со временем я рассмотрю разные варианты, но пока я буду жить с этим решением. Спасибо.
@PavelSmirnov Я просто не чувствую, что это правильный подход к проблеме. Жизнь — это реальная сетка, и поэтому общественная Жизнь должна быть конструктором этой сетки. Ваш метод будет создавать новую сетку при каждом вызове и присваивать статусу одно значение сетки в точках x, y. Не вижу в этом пользы. Традиционный объектный класс с конструктором, сеттером и геттером для каждого элемента в сетке гораздо полезнее, и для тех, кто плохо знаком с java, это важно понять.
@Joel: why i need to instantiate my grid with the gridsize...
вы не объявите это static
и инициализируете его один раз в объявлении
@brandonx, если Life
— это всего лишь одна ячейка, то да, сетка должна создаваться только один раз. Как статическое поле или отдельный объект, что угодно. Совершенно непонятно, что здесь такое Life
, но я согласен, сеттеры и геттеры были бы лучшим подходом.
@PavelSmirnov, я имел в виду, что при таком подходе нет возможности дополнительно изменить или оценить сетку после первоначального создания. Кроме того, создание экземпляра массива и установка одного элемента этого массива в конструкторе не имеет особого смысла.
@Joel Я бы также добавил, что объем памяти, необходимый для логического 2d-массива, тривиален, пока вы не достигнете огромного размера. Я бы не стал слишком беспокоиться об этом.
Не уверен, что я делаю неправильно, но я почти всегда получаю: Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -2
с таким подходом. Я увеличил размер сетки и вызываю его, используя второй пример с getStatus.
@ Джоэл, ты пытаешься получить ячейку с индексом -2, которой явно не существует. Вы должны проверить свой код и убедиться, что вы передаете правильные параметры в метод.
@PavelSmirnov Хорошо, как тогда я могу проверить значения соседних ячеек? Я делаю getStatus(x+1)(y+1) :( Очевидно, что он не существует. Но как я могу предотвратить сбой этой проверки?
@ Джоэл, поскольку вы знаете размер массива, вам следует прекратить увеличивать x и y, как только вы достигнете границы.
Проблема вызвана тем, что Boolean
по умолчанию равно null
. Таким образом, когда вы пытаетесь сделать:
if (!this.grid[x][y]) {
ты действительно делаешь
if (!null) {
что является недопустимым Java и приводит к NullPointerException
. Если вы хотите, чтобы элементы вашей сетки по умолчанию имели значение false
, используйте boolean[][]
вместо Boolean[][]
, поскольку примитивы boolean
не могут быть нулевыми, а по умолчанию — false
:
public class Life {
static boolean[][] grid;
public Life(int x, int y, boolean status) {
this.grid[x][y] = status;
}
}
Если ваш конструктор потребности принимает объект Boolean
, вы можете использовать тройку для проверки на нуль:
public Life(int x, int y, Boolean status) {
this.grid[x][y] = status == null ? false : status;
}
На самом деле, я хотел проверить, был ли он создан. Плохая привычка делать !var
из С#, я думаю. Кроме того, ваши последние два примера дают сбой.
Вы смешиваете конструктор с сеттером. Похоже, вы пытаетесь установить для всей сетки любой статус. Это не сработает по нескольким причинам. Прежде всего:
this.grid[x][y] = status;
на самом деле пытается установить элемент в координатах x, y в статус. На самом деле он попытается сделать это, когда вы создадите новую сетку, и вернет исключение массива за пределами границ из-за нулевой индексации. Если вы установите сетку размером 1, 1, а затем попытаетесь получить доступ к элементу [1, 1], этого не существует. единственный элемент в сетке [1, 1] — это [0, 0].
Вам нужно отделить метод конструктора и сеттера следующим образом:
public class Life {
Boolean[][] grid;
public Life(int xMax, int yMax) {
this.grid = new Boolean[xMax][yMax];
}
public void setStatus(int x, int y, Boolean status) {
this.grid[x][y] = status;
}
public boolean getStatus(int x, int y) {
return this.grid[x][y];
}
}
редактировать: как упоминают другие, если вы хотите, чтобы ваша сетка по умолчанию была ложной, вы должны использовать логическое значение, а не логическое значение.
Вы вызываете эту строку:
if (!this.grid[x][y]) {
В неинициализированной матрице, объявленной в первой строке:
Boolean[][] grid;
Вы также переопределяете значение сетки для каждого нового значения, которое вы добавляете, что, я почти уверен, не является вашим намерением.
Сначала вы должны инициализировать матрицу в конструкторе, например:
public Life(int x, int y) {
grid = new boolean[x][y];
}
Затем только ссылки на значения, хранящиеся следующим образом:
public addValue(int x, int y, Boolean status) {
grid[x][y] = status;
}
В конструкторе Вы пытаетесь получить доступ к индексу в массиве, который не был инициализирован. Вот почему вы получаете NPE.