用Java填充布尔数组
作为一个相当绿色的Java代码编写者,我为自己写了一个简单的文本冒险设置了巨大的挑战。 不出所料,我已经遇到困难了!
我试图给我的Location类属性来存储它包含的出口。 为此,我使用了一个布尔数组,基本上保存表示每个出口的真/假值。 我并不完全相信这一点
a)这是做这件事最有效的方法
b)我使用正确的代码填充数组。
我会很感激任何和所有的反馈,即使它是一个完整的代码over-haul!
目前,当实例化一个位置时,我生成一个String,我通过setExits方法发送:
String e = "NSU"; 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方法…
任何帮助将受到欢迎!
是否有任何理由,你为什么这样用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以不同的方式完成事情。
备查
代码的工作属于Code Review ,而不是Stack Overflow。 虽然@kajacx指出,这个代码不应该 – 事实上的工作。
最有效和最具performance力的方式如下:
使用enum
s作为Exits并使用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
枚举集合将在一个单独的内部存储所有的退出,所以你的代码是expression,快速,并节省了大量的内存。
好的,首先,你的setExits()
方法不能按预期的方式工作,如果setExits()
-else将最大限度地执行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"); ...
此外,我definetly不会记得,北在索引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"); ...
(更可读)
最后,如果你希望你的代码更好地安排,你可以创build一个Exits
类来表示可用的退出,并且由布尔数组支持。 然后在你创build你的string的地方,你可以创build这个类来保存你自己的工作,然后parsing一个string。
编辑:
作为@gexicide的答案,有一个非常方便的类EnumSet
这可能会更好地表示出口比bollean数组。
在另一个答案中的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 }
我会创build一个Exit枚举,并在Location类中设置一个Exit对象列表。
所以它会是这样的:
public enum Exit { N, S, E, W, U, D } List<Exit> exits = parseExits(String exitString); location.setExits(exits);
考虑到你的代码是什么样的,这是我可以实现的最具可读性的实现:
public class Exits { private static final char[] DIRECTIONS = "NSEWUD".toCharArray(); public static void main(String... args) { String input = "NSE"; boolean[] exits = new boolean[DIRECTIONS.length]; for(int i = 0; i< exits.length; i++) { if (input.indexOf(DIRECTIONS[i]) >= 0) { exits[i] = true; } } } }
这就是说,有一些更清洁的解决scheme可能。 就我个人而言,我会去与枚举和EnumSet
。
顺便说一下,你的原始代码是不正确的,因为它会将数组中的最多一个值设置为true。
如果您将出口定义为string,则应该使用它。 我会这样做:
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]。 它是北方还是南方呢? 等等
或者你可以使用枚举和可用的退出列表。 然后在methidtesting中,如果列表包含某个枚举值
就我个人而言,我认为你可以使用枚举来绕过它,然后进行如下操作:
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; } } } }
虽然把它写下来了,但我真的不能告诉你它是否好多了。 增加新的方向比较容易,这是肯定的。
答案中列出的所有方法都很好。 但是我认为您需要采取的方法取决于您将要使用退出字段的方式。 例如,如果要将string作为string处理,那么Ross Drews方法将需要很多if-else条件和variables。
String exit = "NE"; 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; } }
所以如果你想把它作为一个string来操作,我认为最好的办法是去列表和枚举。 通过这种方式,你可以做很多方法,如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 = "NEW"; Location location = new Location(); location.setExits(exits); System.out.println(location.getExits()); System.out.println(location.hasExit('W')); System.out.println(location.hasAllExit("NW")); System.out.println(location.hasAnyExit("UD")); System.out.println(location.hasNorthExit()); } }
为什么不这样做,如果你想要一个短代码:
String symbols = "NWSEUD"; public void setExits(String e) { for (int i = 0; i < 6; i++) { bexits[i] = e.contains(symbols.charAt(i)); } }
如果你想要一个通用的解决scheme,你可以使用一个映射 ,它从一个键(在你的情况W,S,E ..)映射到一个相应的值(在你的情况下是一个布尔值)。
当你做一个set
,你更新了关键的值。 当你做一个get
,你可以拿一个参数键,并简单地检索该键的值。 这个function已经存在于地图中,称为put和get 。
我真的很喜欢从string分配出口的想法,因为它使得简短和可读的代码。 一旦完成,我不明白你为什么要创build一个布尔数组。 如果你有一个string,只要使用它,虽然你可能想要添加一些validation,以防止意外分配包含不需要的字符的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”接受一个方向列表(不pipe它是如何在内部实现的)。
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; } } }
做同样事情的迭代方式
应该用switch
语句replaceelse if
长链。
只要效率不是问题, Enum
就是存储这些值的最具performance力的方法。 请记住, enum
是一个类,所以创build一个新的枚举与相应的开销相关联。