Неправильно ли использовать порядковое значение Enum для индексации массива в Java?

У меня есть два массива: Стены и Соседи.

public boolean[] walls = new boolean[4];
public Cell[] neighbors = new Cell[4];

и у меня есть Enum:

enum Dir
{
    North,
    South,
    East,
    West
}

Теперь я хотел бы иметь доступ к стенам или соседям по их направлению, поэтому мне не нужно пропускать кучу волшебных индексов.

Однако, когда я читал документацию для Enum.ordinal(), он сказал, что программисты практически не будут использовать этот метод, что заставило меня подумать, что его нельзя использовать таким образом.

Я думал о чем-то вроде:

    List<Dir> availableDirections = new ArrayList<Dir>();
    for(Dir direction : Dir.values())
        if (!Neighbors[direction.ordinal()].Visited)
            availableDirections.add(direction);

или даже:

return Neighbors[Dir.North.ordinal()];

Должен ли я вернуться к использованию статических констант для NORTH, SOUTH, EAST, WEST с установленным им значением индекса или использовать порядковый метод Enum?

Ответ 1

По тангенциальной проблеме может быть лучше использовать EnumMap для ваших соседей:

Map<Dir, Cell> neighbours = 
    Collections.synchronizedMap(new EnumMap<Dir, Cell>(Dir.class));

neighbours.put(Dir.North, new Cell());

for (Map.Entry<Dir, Cell> neighbour : neighbours.entrySet()) {
    if (neighbour.isVisited()) { ... }
}

etc..

BTW: экземпляры Enum должны быть условными, все шапки,

enum Dir {
    NORTH,
    EAST, 
    SOUTH,
    WEST
}

Ответ 2

В документации только говорится, что большинство программистов не будут использовать этот метод. Это один из примеров законного использования. Предполагая, что ваш класс управляет как перечислением, так и массивом, нет причин опасаться метода ordinal() для индексации массива (поскольку вы всегда можете синхронизировать их).

Однако, если ваше использование усложняется, скорее всего, вы захотите использовать EnumMap, как было предложено.

Ответ 3

Вы также можете улучшить перечисление (индекс по часовой стрелке):

enum Dir
{
  NORTH(0),
  SOUTH(2),
  EAST(1),
  WEST(3);

  private final int index;

  private Dir(int index) {
    this.index = index;
  }

  public int getIndex() {
    return index;
  }

}

Ответ 4

В JavaDoc говорится:

Большинство программистов не будут использовать Этот метод. Он предназначен для использования сложные данные на основе enum структур, таких как EnumSet и EnumMap.

Я думаю, они хотят сказать, что большинство программистов предпочтут использовать EnumMap или EnumSet для ручного индексирования в массив. Они, конечно же, не означают, что вы должны заменить пару целочисленных переменных для вашего перечисления, так как вы потеряете безопасность типа при этом.

Если вам нужна гибкость, чтобы иметь возможность переупорядочивать константы перечисления без влияния на порядок в массиве, вы можете выделить индекс в другое поле перечисления, как описано Arne.

Ответ 5

Если вы не сохраняете массивы или каким-либо другим образом заставляете себя зависить от разных версий вашего класса enum, безопасно использовать ordinal().

Если вы хотите, не хотите полагаться на неявное упорядочение значений перечисления, вы можете ввести значение частного индекса:

public enum Direction {
  NORTH(0),
  SOUTH(1),
  EAST(2),
  WEST(3);

  private int _index;

  private Direction (int index_)
  {
    _index = index_;
  }

  public int getIndex()
  {
    return _index;
  }
}

Отсюда легко упростить поиск индекса в направлении (создавая карту в статическом блоке для компактной персистентности; проверяйте уникальность в статическом блоке и т.д.

Ответ 6

Использование порядкового номера enum для вашего использования зависит от неявного порядка. Мне нравится быть особенно явным, если вы используете целые значения как индекс в свой массив, связывающий значение со значением.

В этом случае я бы предпочел использовать final static int NORTH = 0 и т.д.

Ответ 7

Я настоятельно рекомендую против него, потому что исходное значение основано на порядке в вашем Java-коде.

Использование ordinal() сделает ваш код очень трудным для поддержания, особенно если какая-то форма упорства входит в уравнение.

например, если вы решите добавить диагональные направления, такие как NORTH_WEST, SOUTH_EAST. если вы используете ordinal(), вы должны добавить их в нижней части своего списка, иначе то, что раньше было SOUTH, могло стать СЕВЕРНЫМ.

т.е. вы можете изменить свой enyum без (возможно) меняющейся функциональности

N  (is now 0 was 0) 
NE (is now 1) 
E (is now 2 was 1) 
SE (is now 3 ) 
S  (is mow 4 was 2) 
SW (is now 5) 
W  (is now 6 was 3)