Заполнение булева массива в Java

Как довольно зеленый Java-кодер, я поставил перед собой сложную задачу попытаться написать простое текстовое приключение. Неудивительно, что я уже столкнулся с трудностями!

Я пытаюсь дать моему классу Location свойство для хранения, которое выходит из него. Я использовал для этого булевский массив, чтобы по существу иметь значения true/false, представляющие каждый выход. Я не совсем уверен, что

a) это самый эффективный способ сделать это и

b), что я использую правильный код для заполнения массива.

Я был бы признателен за любые отзывы, даже если это за полный код перетаскивания!

В настоящее время при создании экземпляра Location я генерирую String, который я отправляю методу setExits:

    String e = "N S U";
    secretRoom.setExits(e);

В классе Location, setExits выглядит следующим образом:

public void setExits(String e) {
    if (e.contains("N"))
        bexits[0] = true;
    else if (e.contains("W"))
        bexits[1] = true;
    else if (e.contains("S"))
        bexits[2] = true;
    else if (e.contains("E"))
        bexits[3] = true;
    else if (e.contains("U"))
        bexits[4] = true;
    else if (e.contains("D"))
        bexits[5] = true;
}

Буду честным, я думаю, что это выглядит особенно неуклюжим, но я не мог придумать другого способа сделать это. Я также не совсем уверен, как написать метод getExits...

Любая помощь будет приветствоваться!

Ответ 1

Есть ли причина, по которой вы делаете это с помощью String и не проходите в booleans, т.е.

public void setExits(boolean N, boolean E, boolean S, boolean W, boolean U, boolean D) 

Или с setters?

public void setNorthOpen(boolean open)
{
  bexits[4] = open;
}

Во-вторых, почему вы храните выходы в виде массива булевых, это небольшой конечный набор, почему не просто

boolean N,S,E,W,U,D;

Так как вам не нужно отслеживать, какое число в массиве имеет каждое направление.

И

Это правильный ответ (если не полностью оптимальный, как у @gexicide), но я полностью призываю всех взглянуть на другие ответы здесь для интересного взгляда на то, как все может быть сделано на Java по-разному.

Для справок в будущем

Код, который работает, принадлежит Обзор кода, а не переполнение стека. Хотя, как указывал @kajacx, этот код не должен работать в действительности.

Ответ 2

Самый эффективный и выразительный способ заключается в следующем:

Используйте enum как "Выход" и используйте EnumSet для их сохранения. EnumSet - эффективная реализация Set, которая использует поле бит для представления констант перечисления.

Вот как вы можете это сделать:

public enum Exit { North, West, South, East, Up, Down; }

EnumSet<Exit> set = EnumSet.noneOf(Exit.class); // An empty set.

// Now you can simply add or remove exits, everything will be stored compactly

set.add(Exit.North); // Add exit
set.contains(Exit.West); // Test if an exit is present
set.remove(Exit.South); //Remove an exit

Enum set будет хранить все выходы в одном long внутренне, поэтому ваш код будет экспрессивным, быстрым и сэкономит много памяти.

Ответ 3

ОК, прежде всего, ваш метод setExits() не будет работать так, как предполагалось, цепочка if-elseif максимально выполнит 1 ветвь кода, например:

if (e.contains("N"))
    bexits[0] = true;
else if (e.contains("W"))
    bexits[1] = true;

Даже если e содержит как N, так и W, будет установлен только bexits[0]. Также этот метод будет только добавлять выходы (например, вызов setExits("") не удалит существующие выходы.

Я бы изменил этот метод на:

bexits[0] = e.contains("N");
bexits[1] = e.contains("W");
...

Кроме того, я определенно не помню, что север находится на индексе 0, на запад на 1,... поэтому обычной практикой является называть ваши индексы с использованием конечных статических констант:

public static final int NORTH = 0;
public static final int WEST = 1;
...

Затем вы можете написать в своем методе setExits:

bexits[NORTH] = e.contains("N");
bexits[WEST] = e.contains("W");
...

(гораздо более читаемый)

Наконец, если вы хотите, чтобы ваш код был еще более упорядоченным, вы можете сделать класс Exits, представляющий доступные выходы, и поддерживаемый логическим массивом. Затем на месте, где вы создаете свою String, вы можете создать этот класс и сохранить свою работу с генерацией и затем разбор строки.

EDIT:

как ответы @gexicide, есть действительно удобный класс EnumSet, который, вероятно, лучше для представления выходов, чем массив bollean.

Ответ 4

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

Как и EnumSet, у вас также есть EnumMap.

Если вы определяете класс комнаты/интерфейс комнаты, то внутри класса комнаты вы можете иметь

Map<Direction, Room> exits = new EnumMap<>(Direction.class);

Теперь вы можете добавить свои ссылки на карту следующим образом:

exits.put(Direction.NORTH, theRoomNorthOfMe);

Тогда ваш код для перемещения между комнатами может быть очень общим:

Room destination=currentRoom.getExit(directionMoved);

if (destination == null) {
    // Cannot move that way
} else {
    // Handle move to destination
}

Ответ 5

Я бы создал перечисление Exit и в классе location просто установил список объектов Exit.

поэтому было бы что-то вроде:

public enum Exit { N, S, E, W, U, D }

List<Exit> exits = parseExits(String exitString);
location.setExits(exits);

Ответ 6

Учитывая, как выглядит ваш код, это самая читаемая реализация, которую я мог бы придумать:

public class Exits {
    private static final char[] DIRECTIONS = "NSEWUD".toCharArray();

    public static void main(String... args) {
        String input = "N S E";
        boolean[] exits = new boolean[DIRECTIONS.length];

        for(int i = 0; i< exits.length; i++) {
            if (input.indexOf(DIRECTIONS[i]) >= 0) {
                exits[i] = true;
            }
        }
    }
}

Таким образом, существует ряд более чистых решений. Лично я бы пошел с перечислениями и EnumSet.

Кстати, ваш исходный код некорректен, так как он установит как самое одно значение в массиве равным true.

Ответ 7

Если вы определяете выходы в виде строки, вы должны использовать ее. Я бы сделал это так:

public class LocationWithExits {
    public static final String NORTH_EXIT="[N]";
    public static final String SOUTH_EXIT="[S]";
    public static final String EAST_EXIT="[E]";
    public static final String WEST_EXIT="[W]";

    private final String exitLocations;

    public LocationWithExits(String exitLocations) {
        this.exitLocations = exitLocations;
    }
    public boolean hasNorthExit(){
        return exitLocations.contains(NORTH_EXIT);
    }
    public static void main(String[] args) {
        LocationWithExits testLocation=new LocationWithExits(NORTH_EXIT+SOUTH_EXIT);
        System.out.println("Has exit on north?: "+testLocation.hasNorthExit());
    }


}

Использование массива логических значений может вызвать множество проблем, если вы забудете, что именно означает bexits [0]. Оставить его на север или на юг? и др.

или вы можете просто использовать перечисления и список доступных выходов. Затем в тесте metid, если список содержит определенное значение перечисления

Ответ 8

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

public void setExits(String e) {
    if (e.contains("N"))
        bexits[0] = true;
    else if (e.contains("W"))
        bexits[1] = true;
    else if (e.contains("S"))
        bexits[2] = true;
    else if (e.contains("E"))
        bexits[3] = true;
    else if (e.contains("U"))
        bexits[4] = true;
    else if (e.contains("D"))
        bexits[5] = true;
}

в

public enum Directions
{
    NORTH("N"),
    WEST("W"),
    SOUTH("S"),
    EAST("E"),
    UP("U"),
    DOWN("D");

    private String identifier;

    private Directions(String identifier)
    {
        this.identifier = identifier;
    }

    public String getIdentifier()
    {
        return identifier;
    }
}

а затем выполните:

public void setExits(String e) 
{
  String[] exits = e.split(" ");
  for(String exit : exits)
  {
      for(Directions direction : Directions.values())
      {
          if(direction.getIdentifier().equals(exit))
          {
              bexits[direction.ordinal()] = true;
              break;
          }
      }
  }
}

Хотя после того, как записал это, я не могу сказать, насколько это лучше. Легче добавить новые направления, что точно.

Ответ 9

Все подходы, перечисленные в ответах, хороши. Но я думаю, что подход, который вам нужно принять, зависит от того, как вы собираетесь использовать поле выхода. Например, если вы собираетесь обрабатывать выход как строки, то подход Росса Дрюса потребует много условий и переменных if-else.

String exit = "N E";
String[] exits = exit.split(" ");
boolean N = false, E = false, S = false, W = false, U = false, D = false;
for(String e : exits){
    if(e.equalsIgnoreCase("N")){
        N = true;
    } else if(e.equalsIgnoreCase("E")){
        E = true;
    } else if(e.equalsIgnoreCase("W")){
        W= true;
    } else if(e.equalsIgnoreCase("U")){
        U = true;
    } else if(e.equalsIgnoreCase("D")){
        D = true;
    } else if(e.equalsIgnoreCase("S")){
        S = true;
    }
}
setExits(N, E, S, W, U, D);

Также, если у вас есть выход, и вы хотите проверить, имеет ли место этот конкретный выход, снова вам нужно будет сделать то же самое

public boolean hasExit(String exit){
    if(e.equalsIgnoreCase("N")){
        return this.N; // Or the corresponding getter method
    } else if(e.equalsIgnoreCase("E")){
        return this.E;
    } else if(e.equalsIgnoreCase("W")){
        return this.W;
    } else if(e.equalsIgnoreCase("U")){
        return this.U;
    } else if(e.equalsIgnoreCase("D")){
        return this.D;
    } else if(e.equalsIgnoreCase("S")){
        return this.S;
    }
}

Итак, если вы собираетесь манипулировать им как строка, то, на мой взгляд, наилучшим подходом было бы перейти на список и перечисление. Таким образом, вы можете делать такие методы, как hasExit, hasAnyExit, hasAllExits, hasNorthExit, hasSouthExit, getAvailableExits и т.д. И т.д. Очень легко. И учитывая количество выходов (6), используя список (или набор), не будет накладными расходами. Например

Enum

public enum EXIT {
        EAST("E"),
        WEST("W"),
        NORTH("N"),
        SOUTH("S"),
        UP("U"),
        DOWN("D");

        private String exitCode;

        private EXIT(String exitCode) {
        this.exitCode = exitCode;
        }

        public String getExitCode() {
        return exitCode;
        }

        public static EXIT fromValue(String exitCode) {
            for (EXIT exit : values()) {
                if (exit.exitCode.equalsIgnoreCase(exitCode)) {
                    return exit;
                }
            }
            return null;
        }

        public static EXIT fromValue(char exitCode) {
            for (EXIT exit : values()) {
                if (exit.exitCode.equalsIgnoreCase(String.valueOf(exitCode))) {
                    return exit;
                }
            }
            return null;
        }
}

Location.java

import java.util.ArrayList;
import java.util.List;


public class Location {

    private List<EXIT> exits;

    public Location(){
        exits = new ArrayList<EXIT>();
    }

    public void setExits(String exits) {
        for(char exitCode :  exits.toCharArray()){
            EXIT exit = EXIT.fromValue(exitCode);
            if(exit != null){
                this.exits.add(exit);
            }
        }
    }

    public boolean hasExit(String exitCode){
        return exits.contains(EXIT.fromValue(exitCode));
    }

    public boolean hasAnyExit(String exits){
        for(char exitCode :  exits.toCharArray()){
            if(this.exits.contains(EXIT.fromValue(exitCode))){
                return true;
            }
        }
        return false;
    }

    public boolean hasAllExit(String exits){
        for(char exitCode :  exits.toCharArray()){
            EXIT exit = EXIT.fromValue(exitCode);
            if(exit != null && !this.exits.contains(exit)){
                return false;
            }
        }
        return true;
    }

    public boolean hasExit(char exitCode){
        return exits.contains(EXIT.fromValue(exitCode));
    }

    public boolean hasNorthExit(){
        return exits.contains(EXIT.NORTH);
    }

    public boolean hasSouthExit(){
        return exits.contains(EXIT.SOUTH);
    }

    public List<EXIT> getExits() {
        return exits;
    }

    public static void main(String args[]) {
        String exits = "N E W";
        Location location = new Location();
        location.setExits(exits);
        System.out.println(location.getExits());
        System.out.println(location.hasExit('W'));
        System.out.println(location.hasAllExit("N W"));
        System.out.println(location.hasAnyExit("U D"));
        System.out.println(location.hasNorthExit());
    }
}

Ответ 10

Почему бы и нет, если вам нужен более короткий код:

String symbols = "NWSEUD";
public void setExits(String e) {
  for (int i = 0; i < 6; i++) {
    bexits[i] = e.contains(symbols.charAt(i));
  }
}

Ответ 11

Если вы хотите использовать общее решение, вы можете использовать map, который отображает из ключа (в вашем случае W, S, E..) до соответствующего значения (в вашем случае логическое).

Когда вы выполните set, вы обновите значение, с которым связан ключ. Когда вы делаете get, вы можете взять ключ аргумента и просто получить значение ключа. Эта функциональность уже существует на карте, называемой put и get.

Ответ 12

Мне очень нравится идея выделения выходов из String, потому что это делает для краткого и читаемого кода. После этого я не понимаю, почему вы хотите создать логический массив. Если у вас есть String, просто используйте его, хотя вы можете добавить некоторую проверку, чтобы предотвратить случайное присвоение строк, содержащих нежелательные символы:

private String exits;

public void setExits(String e) {
    if (!e.matches("[NSEWUD ]*")) throw new IllegalArgumentException();
    exits = e;
}

Единственное, что я хотел бы добавить, это метод canExit, который вы можете вызвать с помощью параметра направления; например, if (location.canExit('N')) ...:

public boolean canExit(char direction) {
    return exits.indexOf(direction) >= 0;
}

Мне нравится перечисление, но использование их здесь кажется чрезмерной для меня, что быстро станет раздражать.


** Изменить **: На самом деле, не делайте этого. Он отвечает на неправильный вопрос, и он делает то, что не нужно делать. Я заметил @TimB ответ с помощью карты (EnumMap), чтобы связать направления с комнатами. Это имеет смысл.

Я все еще чувствую, что если вам нужно только отслеживать существование выхода, String прост и эффективен, и все остальное слишком усложняет его. Однако знать, какие выходы доступны, не полезно. Вы захотите пройти через эти выходы, и если ваша игра не будет иметь очень простой макет, для кода не будет достаточным для того, чтобы вывести правильную комнату для каждого направления, поэтому вам нужно будет прямо связать каждое направление с другой комнатой. Поэтому, по-видимому, нет никакого фактического использования для любого метода "setExits", который принимает список направлений (независимо от того, как он реализован внутри).

Ответ 13

public void setExits(String e)
{
    String directions="NwSEUD";
    for(int i=0;i<directions.length();i++)
    {
        if(e.contains(""+directions.charAt(i)))
        {
            bexits[i]=true;
            break;
        }
    }
}

итеративный способ сделать то же самое.

Ответ 14

Длинные цепочки операторов else if должны быть заменены операторами switch.

Enum являются наиболее выразительным способом хранения таких значений, пока эффективность не является проблемой. Имейте в виду, что Enum - это класс, поэтому создание нового перечисления связано с соответствующими служебными данными.