在 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 方法..。

欢迎任何帮助!

11769 次浏览

是否有任何原因使用 String做这个操作而不传入 booleans,即。

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

还是 赛特犬

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

其次,为什么要把出口存储为布尔型数组,这是一个小的有限集,为什么不

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

因此,您不需要跟踪数组中每个方向的数字。

还有

这是一个正确的答案(如果不是像@gexicide 那样完全最优的话) ,但是我完全鼓励大家看看这里的其他答案,以便有趣地看看如何用 Java 以不同的方式完成工作。

以备将来参考

工作的代码属于 守则检讨,而不属于堆栈溢出。尽管如@kajacx 所指出的,这段代码实际上不应该工作。

如果您想要一个通用的解决方案,您可以使用 地图,它从一个键映射(在您的例子中是 W、 S、 E。)到一个相应的值(在你的例子中是一个布尔值)。

当执行 set时,将更新与键相关联的值。在执行 get时,可以获取一个参数键并简单地检索该键的值。这个功能在 map 中已经存在,称为 放下走开

我将创建一个 Exit 枚举,并在 location 类上设置一个 Exit 对象列表。

所以应该是这样的:

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


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

如果您将出口定义为字符串,那么您应该使用它:

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]的确切含义,那么使用布尔值数组可能会导致许多问题。是南边还是北边?等等。

或者您可以只使用枚举和可用的出口列表。然后在 methid 测试 list 是否包含某个枚举值

考虑到您的代码的外观,这是我能想到的最具可读性的实现:

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。

就我个人而言,我认为你可以使用一个枚举来修改它,然后转动以下命令:

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

不过写下来之后,我也不知道是不是真的好多了。可以肯定的是,添加新的方向更容易。

最有效的 还有表达方式如下:

使用 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

枚举集将在内部将所有出口存储在单个 long中,因此您的代码具有表达能力,速度快,并且节省了大量内存。

好的,首先,您的 setExits()方法不会像预期的那样工作,链式 if-elseif 将最大限度地执行一个代码分支,例如:

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

即使 e同时包含 NW,也只能设置 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 的地方,您可以创建这个类,并节省生成和解析字符串的工作。

编辑:

正如@gexicide 所回答的,有一个非常方便的类 EnumSet,它可能比布尔数组更适合表示出口。

答案中列出的所有方法都是好的。但是我认为你需要采取的方法取决于你使用退出字段的方式。例如,如果你将退出作为字符串处理,那么 Ross Drews 方法将需要大量的 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;
}
}

因此,如果您要将它作为一个字符串来操作,在我看来,最好的方法是使用 list 和 enum。通过这种方式,您可以执行 hasExit、 hasAnyExit、 hasAllExits、 hasNorthExit、 hasSouthExit、 getApplicableExits 等方法。非常容易。并且考虑使用列表(或集合)的出口数(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;
}
}

地点 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());
}
}

另一个答案中的 EnumSet是做到这一点的最好方法,我只是想为将来增加一件事,当你开始不仅考虑你是否可以移动,而且考虑你要移动到哪里。

除了 EnumSet,你还有 EnumMap

如果你定义了一个 Room 类/接口,那么在 Room 类中你可以有

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
}

我非常喜欢从 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;
}

我喜欢 enum,但在这里使用它们对我来说就像是过度设计,很快就会变得很烦人。


编辑: 事实上,不要这样做。它回答了错误的问题,做了一些不需要做的事情。我刚注意到 @ TimB 的回答使用了一个 map (一个 EnumMap)来将方向与房间关联起来。有道理。

我仍然觉得,如果您只需要跟踪 exit 存在,那么 String 是简单而有效的,其他任何东西都会使它过于复杂。但是,只知道哪些出口可用是没有用的。你会想通过这些出口,除非你的游戏有一个非常普通的布局,否则代码不可能推断出每个方向的正确房间,所以你需要明确地将每个方向与另一个房间关联起来。因此,任何方法“ setExits”似乎没有实际用途,它接受一个方向列表(不管它在内部是如何实现的)。

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

做同样事情的迭代方式。

如果您想要更短的代码,为什么不这样做呢:

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

else if语句的长链应该替换为 switch语句。

只要不考虑效率,Enum就是存储这些值的最有表现力的方式。请记住,enum是一个类,因此新枚举的创建与相应的开销相关联。