Двумерный логический массив для хранения значений координат

Я создаю пошаговую игру. В этой игре у меня есть сетка размером 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)

В конструкторе Вы пытаетесь получить доступ к индексу в массиве, который не был инициализирован. Вот почему вы получаете NPE.

Dominik Wosiński 08.04.2019 20:43

@DominikWosiński Понятно, но как бы я, например. установить grid[0][0] = true ?

Joel 08.04.2019 20:44

@Joel: используйте примитив boolean, который по умолчанию является false. Boolean является объектом и находится null в вашей сетке

Cratylus 08.04.2019 20:46

@Cratylus, к сожалению, я получаю ту же ошибку.

Joel 08.04.2019 20:47

@Joel: вы не инициализировали массив. Проверить ответ Павла Смирнова

Cratylus 08.04.2019 20:52

@Joel: Также grid должно быть static

Cratylus 08.04.2019 20:53

когда вы говорите if (!this.grid[x][y]), вы пытаетесь получить доступ к элементу [x][y], но массив еще не инициализирован. Вместо этого это должно быть просто if (this.grid != null)

Chris Rollins 08.04.2019 21:07

и Кратил, вероятно, прав. Если вы хотите, чтобы каждый объект Life имел одну и ту же сетку, то он должен быть помечен static. это, кажется, ваше намерение.

Chris Rollins 08.04.2019 21:08

Я думаю, что здесь все пытаются изобрести велосипед, когда Life должен быть очень простым объектным классом с методами setStatus и getStatus для доступа к элементам в массиве. Мы не должны устанавливать какие-либо статусы в конструкторе.

brandonx 08.04.2019 21:26
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
9
379
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы обращаетесь к полю 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.. В любом случае. Интересно, но я действительно не понимаю Зачем, мне нужно создать экземпляр моей сетки с размером сетки... хм. Думал, что смогу просто создать экземпляр одной ячейки за раз.

Joel 08.04.2019 20:53

@ Джоэл, прежде чем вы сможете использовать какую-либо ячейку в массиве 2d, вы должны сначала создать экземпляр всего массива, выделить память для всех значений. Если вы хотите создать только одну ячейку за раз и получить к ней доступ с помощью составного ключа x и y, вам, вероятно, следует взглянуть на другую структуру данных, например, HashMap, где x и y — ключи, а Boolean — значение. .

Pavel Smirnov 08.04.2019 20:56

@Joel, вы можете установить размер массива, используя параметры x и y, а не целочисленные литералы в этом ответе.

brandonx 08.04.2019 20:58

Угу... не уверен, что мне нравится, как с этим справляется Java. Злоупотребление памятью .. Было бы намного лучше, если бы я мог выделить место в любом месте массива, как мне заблагорассудится. В любом случае, спасибо за ответ. Одобренный.

Joel 08.04.2019 20:59

@brandonx, я полагаю, x и y - это просто координаты для установки логического значения, а не размера всего массива.

Pavel Smirnov 08.04.2019 21:00

@ Джоэл, пожалуйста :) Вы также можете рассмотреть возможность использования List<List<Boolean>>. Это почти то же самое, но хм... это изменяемый массив логических значений. При таком подходе вы можете получить доступ к значениям с помощью list.get(x).get(y); Но это намного больше потребляет памяти.

Pavel Smirnov 08.04.2019 21:02

@PavelSmirnov ваше предположение верно. Вот почему мне трудно это переварить. Со временем я рассмотрю разные варианты, но пока я буду жить с этим решением. Спасибо.

Joel 08.04.2019 21:04

@PavelSmirnov Я просто не чувствую, что это правильный подход к проблеме. Жизнь — это реальная сетка, и поэтому общественная Жизнь должна быть конструктором этой сетки. Ваш метод будет создавать новую сетку при каждом вызове и присваивать статусу одно значение сетки в точках x, y. Не вижу в этом пользы. Традиционный объектный класс с конструктором, сеттером и геттером для каждого элемента в сетке гораздо полезнее, и для тех, кто плохо знаком с java, это важно понять.

brandonx 08.04.2019 21:11

@Joel: why i need to instantiate my grid with the gridsize... вы не объявите это static и инициализируете его один раз в объявлении

Cratylus 08.04.2019 21:11

@brandonx, если Life — это всего лишь одна ячейка, то да, сетка должна создаваться только один раз. Как статическое поле или отдельный объект, что угодно. Совершенно непонятно, что здесь такое Life, но я согласен, сеттеры и геттеры были бы лучшим подходом.

Pavel Smirnov 08.04.2019 21:16

@PavelSmirnov, я имел в виду, что при таком подходе нет возможности дополнительно изменить или оценить сетку после первоначального создания. Кроме того, создание экземпляра массива и установка одного элемента этого массива в конструкторе не имеет особого смысла.

brandonx 08.04.2019 21:20

@Joel Я бы также добавил, что объем памяти, необходимый для логического 2d-массива, тривиален, пока вы не достигнете огромного размера. Я бы не стал слишком беспокоиться об этом.

brandonx 08.04.2019 21:22

Не уверен, что я делаю неправильно, но я почти всегда получаю: Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: -2 с таким подходом. Я увеличил размер сетки и вызываю его, используя второй пример с getStatus.

Joel 08.04.2019 21:50

@ Джоэл, ты пытаешься получить ячейку с индексом -2, которой явно не существует. Вы должны проверить свой код и убедиться, что вы передаете правильные параметры в метод.

Pavel Smirnov 08.04.2019 22:04

@PavelSmirnov Хорошо, как тогда я могу проверить значения соседних ячеек? Я делаю getStatus(x+1)(y+1) :( Очевидно, что он не существует. Но как я могу предотвратить сбой этой проверки?

Joel 08.04.2019 22:18

@ Джоэл, поскольку вы знаете размер массива, вам следует прекратить увеличивать x и y, как только вы достигнете границы.

Pavel Smirnov 08.04.2019 22:23

Проблема вызвана тем, что 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 из С#, я думаю. Кроме того, ваши последние два примера дают сбой.

Joel 08.04.2019 20:55

Вы смешиваете конструктор с сеттером. Похоже, вы пытаетесь установить для всей сетки любой статус. Это не сработает по нескольким причинам. Прежде всего:

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;
}

Другие вопросы по теме