Populating a Boolean Array in Java - java

As a fairly green Java coder I've set myself the hefty challenge of trying to write a simple text adventure. Unsurprisingly, I've encountered difficulties already!
I'm trying to give my Location class a property to store which exits it contains. I've used a boolean array for this, to essentially hold true/false values representing each exit. I'm not entirely convinced that
a) this is the most efficient way to do this and
b) that I'm using the right code to populate the array.
I would appreciate any and all feedback, even if it is for a complete code over-haul!
At present, when instantiating a Location I generate a String which I send through to the setExits method:
String e = "N S U";
secretRoom.setExits(e);
In the Location class, setExits looks like this:
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;
}
I'll be honest, I think this looks particularly clunky, but I couldn't think of another way to do it. I'm also not entirely sure now how to write the getExits method...
Any help would be welcome!

The most efficient and expressive way is the following:
Use enums as Exits and use an EnumSet to store them. EnumSet is an efficient Set implementation that uses a bit field to represent the enum constants.
Here is how you can do it:
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 will store all exits in a single long internally, so your code is expressive, fast, and saves a lot of memory.

Is there any reason why you are doing this with Strings and aren't passing in booleans, i.e.
public void setExits(boolean N, boolean E, boolean S, boolean W, boolean U, boolean D)
Or having setters?
public void setNorthOpen(boolean open)
{
bexits[4] = open;
}
Secondly, why are you storing the exits as an array of booleans, it's a small finite set, why not just
boolean N,S,E,W,U,D;
As then you don't need to keep track of which number in the array each direction is.
Also
This is a correct answer (if not completely optimal like that of #gexicide) but I fully encourage anyone to look at the other answers here for an interesting look at how things can be done in Java in different ways.
For future reference
Code which works belongs on Code Review, not Stack Overflow. Although as #kajacx pointed out, this code shouldn't -in fact- work.

OK, first of all, your setExits() method will not work as intended, chained if-elseif will maximally execute 1 branch of code, for example:
if (e.contains("N"))
bexits[0] = true;
else if (e.contains("W"))
bexits[1] = true;
Even if e contains both N and W, only bexits[0] will be set. Also this method will only add exits (for example calling setExits("") will not delete any existing exits.
I would change that method to:
bexits[0] = e.contains("N");
bexits[1] = e.contains("W");
...
Also, i definetly wouldn't remember that north is on index 0, west in on 1, ... so a common practice is to name your indexes using final static constants:
public static final int NORTH = 0;
public static final int WEST = 1;
...
Then you can write in your setExits method:
bexits[NORTH] = e.contains("N");
bexits[WEST] = e.contains("W");
...
(much more readible)
Finally, if you want your code even more well-arranged, you can make a Exits class representing avaliable exits, and backed by boolean array. Then on place where you create your String, you could create this class instead and save yourself work with generating and then parsing a string.
EDIT:
as #gexicide answers, there is a really handy class EnumSet which would be probably better for representing the exits than bollean array.

The EnumSet in the other answer is the best way to do this, I just wanted to add one more thing though for the future when you start looking not just at whether you can move but where you are moving to.
As well as EnumSet you also have EnumMap.
If you define a Room class/interface then inside the Room class you can have
Map<Direction, Room> exits = new EnumMap<>(Direction.class);
You can now add your links into the map as follows:
exits.put(Direction.NORTH, theRoomNorthOfMe);
Then your code to move between rooms can be very general purpose:
Room destination=currentRoom.getExit(directionMoved);
if (destination == null) {
// Cannot move that way
} else {
// Handle move to destination
}

I would create an Exit enum and on the location class just set a list of Exit objects.
so it would be something like:
public enum Exit { N, S, E, W, U, D }
List<Exit> exits = parseExits(String exitString);
location.setExits(exits);

Given what your code looks like, this is the most readable implementation I could come up with:
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;
}
}
}
}
That being said, there's a number of cleaner solutions possible. Personally I would go with enums and an EnumSet.
By the way, your original code is incorrect, as it will set as most one value in the array to true.

If you're defining exits as a string, you should use it. I would do it like:
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());
}
}
using array of booleans might cause a lot of problems if you forget what exactly means bexits[0]. Os it for north or south? etc.
or you can just use enums and list of exits available . Then in methid test if list contain a certain enum value

Personally, I think you can hack it around a bit using an enum and turn the following:
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;
}
into
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;
}
}
and then do:
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;
}
}
}
}
Although after having written it down, I can't really tell you if it's that much better. It's easier to add new directions, that's for sure.

All the approaches listed in the answeres are good. But I think the approach you need to take depends on the way you are going to use the exit field. For example if you are going to handle exit as strings then Ross Drews approach would require a lot of if-else conditions and variables.
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);
Also if you have an exit and you want to check whether a location has that particular exit then again you will have to do the same
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;
}
}
So if you are going to manipulate it as a string, in my opinion the best approach would be to go for list and enum. By this way you could do methods like hasExit, hasAnyExit, hasAllExits, hasNorthExit, hasSouthExit, getAvailableExits etc etc.. very easily. And considering the number of exits (6) using a list (or set) wont be an overhead. For example
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());
}
}

Why not this if you want a shorter code:
String symbols = "NWSEUD";
public void setExits(String e) {
for (int i = 0; i < 6; i++) {
bexits[i] = e.contains(symbols.charAt(i));
}
}

If you want a generic solution you can use a map, which maps from a key (in your case W, S, E.. ) to a corresponding value (in your case a boolean).
When you do a set, you update the value the key is associated with. When you do a get, you can take an argument key and simply retrieve the value of the key. This functionality does already exist in map, called put and get.

I really like the idea of assigning the exits from a String, because it makes for brief and readable code. Once that's done, I don't see why you would want to create a boolean array. If you have a String, just use it, although you might want to add some validation to prevent accidental assignment of strings containing unwanted characters:
private String exits;
public void setExits(String e) {
if (!e.matches("[NSEWUD ]*")) throw new IllegalArgumentException();
exits = e;
}
The only other thing I would add is a method canExit that you can call with a direction parameter; e.g., if (location.canExit('N')) ...:
public boolean canExit(char direction) {
return exits.indexOf(direction) >= 0;
}
I like enums, but using them here seems like over-engineering to me, which will rapidly become annoying.
**Edit**: Actually, don't do this. It answers the wrong question, and it does something which doesn't need to be done. I just noticed #TimB's answer of using a map (an EnumMap) to associate directions with rooms. It makes sense.
I still feel that if you only need to track exit existence, a String is simple and effective, and anything else is over-complicating it. However, only knowing which exits are available isn't useful. You will want to go through those exits, and unless your game has a very plain layout it won't be doable for the code to infer the correct room for each direction, so you'll need to explicitly associate each direction with another room. So there seems to be no actual use for any method "setExits" which accepts a list of directions (regardless of how it's implemented internally).

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;
}
}
}
the iterative way of doing the same thing..

Long chains of else if statements should be replaced with switch statements.
Enums are the most expressive way to store such values as long as the efficiency is not a concern. Keep in mind that enum is a class, so creation of a new enum is associated with corresponding overhead.

Related

Setting a value to zero

Ok so in my cs class we have an assignment that requires us to return a value and then set it to zero. I can't figure out how to do this without using a secondary variable(which would break requirements) so I would appreciate some help. here are the exact requirements.
"Has a use() method that returns the value contained in the points field. It also resets the points field to zero. You’re going to have to think about the order of operations here to make this work correctly."
package Game;
import java.util.Random;
public class HealthPotion
{
private int points;
boolean Haspotion;
HealthPotion()
{
Random num1 = new Random();
int num = num1.nextInt(10)+1;
points=num*10;
}
public int Use()
{
return points;
}
public int getPoints()
{
return points;
}
}
That's not really possible without abusing a finally block, i.e.
try {
return points;
} finally {
points = 0;
}
However it's really hard to believe that would be what's wanted, since it's not a good idea to write code like that.
Include a setter method like this.
public void setValue(){
this.points=0;
}
Call this method after you get the value.
How about this?
public int Use()
{
int tmp = points;
points = 0;
return tmp;
}
It has limitations, especially if points can be changed by a different thread while this method executes. But if you are working in a single-threaded environment this should be ok.
This should work
int points = 5;
public void test(){
System.out.println(use() +" " + points);
}
private int use(){
return points - (points = 0);
}
returning 5 0

Proper way testing that String belongs to subset of constants (Java)

Goal:
Represent subset of Strings created from Strings defined in abstract class
test if string on input belongs to given subset
Initial solution:
Let's have list of possible events.
/**
* List of events.
*/
public abstract class EventKeys {
public static final String KEY_EVENT_1 = "EVENT_1";
public static final String KEY_EVENT_2 = "EVENT_2";
public static final String KEY_EVENT_3 = "EVENT_3";
public static final String KEY_EVENT_4 = "EVENT_4";
public static final String KEY_EVENT_5 = "EVENT_5";
public static final String KEY_EVENT_6 = "EVENT_6";
public static final String KEY_EVENT_7 = "EVENT_7";
//etc ..
}
I want make subset of these events for example events 1,3,5 and only for these events allow some action. The goal is make method boolean isEventAllowed(String eventKey) which will say if event belongs to subset of allowed events.
The really naive way to do this is:
/**
* Allow only events 1,3,5
*/
private isEventAllowed(String eventKey) {
if(eventKey.equals(EventKeys1.KEY_EVENT_1)) {
return true;
} else if(eventKey.equals(EventKeys1.KEY_EVENT_3)) {
return true;
} else if(eventKey.equals(EventKeys1.KEY_EVENT_3)) {
return true;
} else {
return false;
}
}
The I feel this approach is not very convinient. I need better way to represent the subset of strings and provide action does input string belongs to defined subset?
Other possible solutions:
As other options i was thinking about other two options, but I'm still not sure if its good way to do it.
1)enum - create enum of strings
Put in enum: EventKeys1.KEY_EVENT_1, EventKeys1.KEY_EVENT_2, EventKeys1.KEY_EVENT_3
Test does String keyEvent belons to defined enum?
2) list
create list List<String> subset and put there
EventKeys1.KEY_EVENT_1, EventKeys1.KEY_EVENT_2, EventKeys1.KEY_EVENT_3
test if String keyEvent belongs to list subset
PLEASE READ THIS BEFORE ANSWER:
class EventKeys is given, can't be changed, main set of options
I need somehow represent subset
I need advice for better implementation of method isAllowedEvent(String keyEvent) which returns true if input string
belongs to defined subset
How about something like this?
private boolean isEventAllowed(String eventKey) {
return Arrays.asList(KEY_EVENT_1, KEY_EVENT_3, KEY_EVENT_5).contains(eventKey);
}
Readability could be improved following John Fergus' comment by using something like this:
private static List<String> SUBSET = Arrays.asList(KEY_EVENT_1, KEY_EVENT_3, KEY_EVENT_5);
private boolean isEventAllowed(String eventKey) {
return SUBSET.contains(eventKey);
}
While a Set holding the allowed values is usually the preferred option, there are also possible syntactical improvements for your original code which you should become aware of, as their general pattern applies to other situations as well.
A statement like
if(condition1)
action;
else if(condition2)
/* (same) */ action;
…
is redundant and may be replaced by
if(condition1 || condition2)
action;
…
similarly
if(condition)
return true;
else
return false;
is redundant and may (or even should) be replaced by
return condition;
Putting both together, your original code becomes
private boolean isEventAllowed(String eventKey) {
return eventKey.equals(EventKeys1.KEY_EVENT_1)
|| eventKey.equals(EventKeys1.KEY_EVENT_3)
|| eventKey.equals(EventKeys1.KEY_EVENT_5);
}
Alternatively, you can use a switch statement:
private boolean isEventAllowed(String eventKey) {
switch(eventKey) {
case EventKeys1.KEY_EVENT_1:
case EventKeys1.KEY_EVENT_3:
case EventKeys1.KEY_EVENT_5:
return true;
default:
return false;
}
}
Not everyone likes this coding style, but that’s more an issue of project or company policies. There are situation, where such a switch statement still is the cleanest solution. One advantage over if statements and even the Set approach is that the compiler will immediately shout if you mistakenly name the same constant twice rather than the intended constant (a typical copy&paste error), like you do in your third if statement where you use KEY_EVENT_3 instead of the intended KEY_EVENT_5…

Minecraft Forge Custom Player Command Issues

I am trying to make a new command for the first time and was following this tutorial
which is slightly old but I believe will still work. After finishing it I tried running my mod and everything ran fine but my command did not exist. Here is my code:
public class MainRegistry {
#EventHandler
public void serverStart(FMLServerStartingEvent event) {
MinecraftServer server = MinecraftServer.getServer();
ICommandManager command = server.getCommandManager();
ServerCommandManager manager = (ServerCommandManager) command;
manager.registerCommand(new FireBall5());
}
}
And my actual CommandBase class:
public class FireBall5 extends CommandBase {
#Override
public String getCommandName() {
return "fireball 5";
}
#Override
public String getCommandUsage(ICommandSender var1) {
return "Shoots fireball with explosive power 5";
}
#Override
public void processCommand(ICommandSender icommandsender, String[] var2) {
if (icommandsender instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) icommandsender;
World par2World = player.worldObj;
if (!par2World.isRemote)
par2World.spawnEntityInWorld(new PlayerFireBall(par2World, 5.0f));
}
}
}
It is calling an entity PlayerFireBall which I created myself and is simply a fireball with increased explosion power.
Commands cannot contain whitespaces. To implement your command, please follow the following:
#Override
public String getCommandName() {
return "fireball"; // Remove the argument - Leave the command only.
}
The argument has to be read like this instead:
{
if (sender instanceof EntityPlayer) {
final EntityPlayer player = (EntityPlayer) sender;
final World par2World = player.worldObj;
final float power;
// The "default" method:
// power = 5; // Write the default value here!
if (var2.length > 0) try {
power = Float.parseFloat(var2[0]); // Parse the first argument.
} catch(NumberFormatException ex) {}
// The "validation" method:
if (var2.length == 0) {
sender.sendMessage("You forgot to specify the fireball power.");
return;
}
if ( !var2[0].matches("\\d{2}")) { // Asserts this argument is two digits
sender.sendMessage("Incorrect.");
return;
}
power = Float.parseFloat(var2[0]);
if ( !par2World.isRemote)
par2World.spawnEntityInWorld(new PlayerFireBall(par2World, power));
}
}
Read more:
Reading arguments as Integer for a Bounty in a Bukkit plugin
See #Unihedron answer for the fix for the actual problem with this code. This answer simply cleans up his code even more.
CommandBase from which you inherit actually has several static methods that make parsing numbers and such from arguments much safer.
The ones you might want to use are:
CommandBase.parseDouble(ICommandSender, String) - Parses the given string and returns a double safely
CommandBase.parseDoubleWithMin(ICommandSender, String, int min) - Same as above, but with a required minimum value
CommandBase.parseDoubleBounded(ICommandSender, String, int min, int max) - Same as above, but with an upper limit as well
All these have an integer counterpart as well.
Also, not useful for your context, but maybe for future use is this:
CommandBase.parseBoolean(ICommandSender, String) - Parses the given string and returns a boolean safely
Look through the CommandBase class for many more useful static methods.
So for example, rather than this:
if (var2.length > 0) try {
power = Float.parseFloat(var2[0]); // Parse the first argument.
} catch(NumberFormatException ex) {}
Try this:
if(var2.length > 0){
//bounded because you don't want a size less than 0, could really be anything
power = CommandBase.parseDoubleWithMin(sender, var2[0], 0);
}
Minecraft will automatically tell the player if there is something wrong with there input and safely return the parsed value to you.
Good luck with your mod and have fun!

Using enum as a parameter

So, I am trying to use an enumerated data type as parameter in the place of an object being passed in. I know that a simple switch statement would work but that doesn't really seem elegant to me. I have searched and found that enums can also have actions attached to them but I'm not so clear how to use it in this case or if it is even possible, or if i am just really tired. let me try to use code to explain what I'm asking.
First I have a class with certain fields of other objects that I am basically trying to use the enums to reference. In this case I have a method that acts on one of the fields of trees, because their are multiple trees the method needs to know which tree to act on.
public class bstContactManage()
{
// fields of other objects
BST searchTreeFirstName = new BST(new ComparatorObjOne);
BST searchTreeLastName = new BST(new ComparatorObjTwo);
// and so on and so forth
public boolean modify(Contact contactToFind, BST ToFindIn, String newContactInfo)
{
Contact contUpdate = new Contact(ContactToFind)//save for readdition to tree
contUpdate.update(newContactInfo);
toFindIn.remove(contactToFind);
if(toFindIn.add(contUpdate)) return true;
else return false;
}
}
what I'm wondering or more or less pondering is how to replace the BST parameter with a an enum
i know i could use a switch statement but that doesn't seem any more effective maybe more elegant than passing it an int value and letting it go wild!
so is there a way to get method to look something like
public boolean modify(Contact contactToFind, Enum BSTType, String newContactInfo)
{
Contact contUpdate = new Contact(ContactToFind)//save for readdition to tree
contUpdate.update(newContactInfo);
BSTType.remove(contactToFind);
if(BSTType.add(contUpdate)) return true;
else return false;
}
most of my question stems from the fact that an object such as
bstContactManage man = new bstContactManage()
will be instantiated in another class, and therefore it isn't safe or doesn't seem proper to me to do something like
man.modify(contactIn, man.searchTreeFirstName, "String");
update:
so for more clarification i have another method find which searches a given BST, and currently i am implementing it like this
public List<Contact> find(BinarySearchTree treeUsed, String findThis)
{
//create a new contact with all fields being the same, find is dependent and comparator on tree;
Contact tempContact = new Contact(findThis, findThis, findThis);
return treeUsed.getEntry(tempContact); // where getEntry returns a list of all matching contacts
}
I could do something like
public List<Contact> find(EnumField field, String findThis)
{
BST treeUsed;
switch(Field){
case FIRST:
treeUsed = this.searchTreeFirstName;
break;
cast LAST:
treeUsed = this.searchTreeLastName;
break;
Contact tempContact = new Contact(findThis, findThis, findThis);
return treeUsed.getEntry(tempContact); // where getEntry returns a list of all matching contacts
}
Enum could provide different implementation of its method. A good example would be Math operation:
enum Op {
PLUS {
int exec(int l, int r) { return l + r; }
},
MINUS {
int exec(int l, int r) { return l - r; }
};
abstract int exec(int l, int r);
}
Then I could do Op.PLUS.exec(5, 7) to perform 5 plus 7
See http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html for more detail on how to use enum.
In your case, I wouldn't use enum for something having loads of logic and state, but here is how you could use enum with methods having different implementations.
enum BSTType {
SearchTreeFirstName {
void someMethod(Contact c) {...}
},
SearchTreeLastName {
void someMethod(Contact c) {...}
};
abstract void somemethod(Contact c);
}
public boolean modify(Contact contactToFind, BSTType bstType, String newContactInfo) {
// ...
bstType.someMethod(contact);
// ...
}
By looking at the variable name and class name, I think what you actually meant is indexing Contact in a TreeSet either by first name or last name
enum IndexType implements Comparator<Contact> {
IndexByFirstName {
#Override
public int compare(Contact o1, Contact o2) {
return o1.firstName.compareTo(o2.firstName);
}
},
IndexByLastName {
#Override
public int compare(Contact o1, Contact o2) {
return o1.lastName.compareTo(o2.lastName);
}
};
}
TreeSet<Contact> contacts = new TreeSet<Contact>(IndexType.IndexByLastName);

Looking to associate strings to ints in a cleaner/more efficient way

How can I improve this?
The relationship is one to one and continuous on [-1,5] so i was thinking of using enum, but I'm not sure how to compare a string value to an enum value.
If there is any better way to do this, please suggest.
Thanks!
private int evaluateWord(String sval) {
if (sval.equals("program"))
return 1;
else if (sval.equals("begin"))
return 2;
else if (sval.equals("end"))
return 3;
else if (sval.equals("int"))
return 4;
else if (sval.equals("if"))
return 5;
else
System.exit(0);
Have you considered stuffing the mapping into a HashMap once, and then just querying the map?
For example, something like this:
private static final Map<String,Integer> m_map = new HashMap<String,Integer>();
static {
m_map.put( "program", 1 );
m_map.put( "begin", 2 );
m_map.put( "end", 3 );
m_map.put( "int", 4 );
m_map.put( "if", 5 );
}
private int evaluateWord(String sval) {
Integer value = m_map.get( sval );
if ( null != value ) {
return value;
}
else {
System.exit(0);
}
}
By the way, it looks as if you're writing a parser. It can be reasonable to write a parser by hand. Another option to consider, unless you have a good reason to write it by hand, is a parser generator like ANTLR.
Using an enumeration:
enum Word {
PROGRAM(1,"program"),
BEGIN(2,"begin"),
END(3,"end"),
INT(4,"int"),
IF(5,"if");
private final int value;
private final String representation;
Word(int value, String representation)
{
this.value = value;
this.representation = representation;
}
public int value()
{ return value; }
private static Map<String, Word> fromRep =
new HashMap<String, EnumExample2.Word>();
public static Word fromRepresentation(String rep) {
if (!validRep(rep)) {
throw new IllegalArgumentException("No rep: "+rep);
}
return fromRep.get(rep);
}
public static boolean validRep(String rep)
{ return fromRep.get(rep) != null; }
static {
for (Word word : Word.values()) {
fromRep.put(word.representation, word);
}
}
}
Then your logic is:
private int evaluateWord(String sval) {
if (!Word.validRep(sval)) {
System.exit(0);
}
return Word.fromRepresentation(sval).value();
}
A hashmap could work:
private static HashMap<String, Integer> lookup = new HashMap<String, Integer>();
static {
lookup.put("program", 1);
lookup.put("being", 2);
lookup.put("end", 3);
lookup.put("int", 4);
lookup.put("if", 5);
}
private int evaluateWord(String sval) {
if ( lookup.containsKey(sval) ) {
return lookup.get(sval);
}
System.exit(0);
}
This is what a map is for;
Create a HashMap, add key and values to the map like
wordMap.put("program", Integer.valueOf(1));
....
then, to get the value do
Integer val = wordMap.get(sval);
Honestly, I wouldn't worry about keeping something like this ultra efficient, but there is a change you could make. If the word you pass is the last word you check for then your program ends up performing all of the checks in your function. This shouldn't be a problem in this case, but generally you don't want to flood your program with if statements, especially if you have a lot of cases.
Use a hashtable and just insert pairs. This way, all of your evaluateWord calls will return in amortized constant time. :)
Good luck!
Why do you need a (very subjective) "cleaner" way?
You could get more efficiency from using a hash lookup but you'd want to be certain it's called quite a bit to make the extra coding effort worthwhile. If it's something that happens infrequently (and, by that, I mean something like less than once a second), it's not worth doing (YAGNI).
One thing you might want to do for better looking code (if that's important) is to ditch the else bits, they're totally unnecessary:
private int evaluateWord(String sval) {
if (sval.equals("program")) return 1;
if (sval.equals("begin")) return 2;
if (sval.equals("end")) return 3;
if (sval.equals("int")) return 4;
if (sval.equals("if")) return 5;
System.exit(0);
}
You could just use an array or hashmap to map the enum values to the string values.
Inspired by your enum comment, I present the following. It's a bit hackish, but:
enum Word
{
PROGRAM (1), BEGIN (2), END (3), INT (4), IF (5);
public int value;
public Word (int value)
{
this.value = value;
}
};
int evaluateWord (String word)
{
return Word.valueOf(word.toUpperCase( )).value;
}
I love Java enums because you can do things like this. This is especially useful if you later want to (for example) add a unique behaviour for each word, or to maintain a long list of words. Note though that it is case insensitive.
Or, alternately:
enum Word
{
PROGRAM, BEGIN, END, INT, IF;
};
int evaluateWord (String word)
{
return Word.valueOf(word.toUpperCase( )).ordinal( ) + 1;
}

Categories