作为一个相当绿色的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方法...
欢迎任何帮助!
答案 0 :(得分:127)
最有效的和表达方式如下:
使用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
中,因此您的代码具有表现力,速度快,并且可以节省大量内存。
答案 1 :(得分:29)
是否有任何理由说明您使用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中完成任务。
供将来参考
有效的代码属于Code Review,而非Stack Overflow。虽然@kajacx指出,这段代码实际上不应该起作用。
答案 2 :(得分:15)
好的,首先,你的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的地方,你可以创建这个类,并保存自己,然后生成然后解析一个字符串。
编辑:
正如@gexicide回答的那样,有一个非常方便的类EnumSet
,它可能更好地表示出口而不是bollean数组。
答案 3 :(得分:9)
另一个答案中的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
}
答案 4 :(得分:7)
我将创建一个Exit枚举,并在location类上设置一个Exit对象列表。
所以它会是这样的:
public enum Exit { N, S, E, W, U, D }
List<Exit> exits = parseExits(String exitString);
location.setExits(exits);
答案 5 :(得分: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。
答案 6 :(得分:3)
如果您将出口定义为字符串,则应使用它。我会这样做:
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测试中,如果列表包含某个枚举值
答案 7 :(得分:3)
就我个人而言,我认为你可以使用枚举来修改它并转向以下内容:
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;
}
}
}
}
虽然在写下来之后,我真的无法告诉你它是否会好得多。添加新方向更容易,这是肯定的。
答案 8 :(得分:3)
答案中列出的所有方法都很好。但我认为您需要采取的方法取决于您将使用退出字段的方式。例如,如果你要将exit作为字符串处理,那么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,getAvailableExits等方法。并且考虑使用列表(或集合)的退出(6)的数量不会是开销。例如
枚举
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());
}
}
答案 9 :(得分:2)
如果您想要更短的代码,为什么不这样做:
String symbols = "NWSEUD";
public void setExits(String e) {
for (int i = 0; i < 6; i++) {
bexits[i] = e.contains(symbols.charAt(i));
}
}
答案 10 :(得分:1)
如果你想要一个通用的解决方案,你可以使用map,它从一个键(在你的情况下是W,S,E ..)映射到一个相应的值(在你的情况下是一个布尔值)。
当您执行set
时,您将更新与该关键字相关联的值。执行get
时,可以使用参数键并只检索键的值。地图中已存在此功能,称为put和get。
答案 11 :(得分:0)
我非常喜欢从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's answer使用地图(EnumMap)将方向与房间相关联。这很有道理。
我仍然觉得如果你只需要跟踪退出存在,一个String就简单而有效,而其他任何东西都过于复杂。但是,只知道哪些出口是可用的并不是有用的。您将需要浏览这些出口,除非您的游戏具有非常简单的布局,否则代码无法为每个方向推断出正确的空间,因此您需要明确关联每个方向和另一个房间。因此似乎没有实际使用任何方法&#34; setExits&#34;它接受一个方向列表(无论它是如何在内部实施的)。
答案 12 :(得分:0)
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;
}
}
}
做同样事情的迭代方式..
答案 13 :(得分:0)
else if
语句的长链应替换为switch
语句。
Enum
是存储此类值的最具表现力的方式,只要效率不是问题。请记住enum
是一个类,因此创建新的枚举与相应的开销相关联。