In my app, I need to branch out if the input matches some specific 20 entries.
I thought of using an enum
public enum dateRule { is_on, is_not_on, is_before,...}
and a switch on the enum constant to do a function
switch(dateRule.valueOf(input))
{
case is_on :
case is_not_on :
case is_before :
.
.
.
// function()
break;
}
But the input strings will be like 'is on', 'is not on', 'is before' etc without _ between words.
I learnt that an enum cannot have constants containing space.
Possible ways I could make out:
1, Using if statement to compare 20 possible inputs that giving a long if statement like
if(input.equals("is on") ||
input.equals("is not on") ||
input.equals("is before") ...) { // function() }
2, Work on the input to insert _ between words but even other input strings that don't come under this 20 can have multiple words.
Is there a better way to implement this?
You can define your own version of valueOf method inside the enum (just don't call it valueOf).
public enum State {
IS_ON,
IS_OFF;
public static State translate(String value) {
return valueOf(value.toUpperCase().replace(' ', '_'));
}
}
Simply use it like before.
State state = State.translate("is on");
The earlier switch statement would still work.
It is possible to seperate the enum identifier from the value. Something like this:
public enum MyEnumType
{
IS_BEFORE("is before"),
IS_ON("is on"),
IS_NOT_ON("is not on")
public final String value;
MyEnumType(final String value)
{
this.value = value;
}
}
You can also add methods to the enum-type (the method can have arguments as well), something like this:
public boolean isOnOrNotOn()
{
return (this.value.contentEquals(IS_ON) || this.value.contentEquals(IS_NOT_ON));
}
Use in switch:
switch(dateRule.valueOf(input))
{
case IS_ON: ...
case IS_NOT_ON: ...
case IS_BEFORE: ...
}
And when you get the value of IS_ON like for example System.out.println(IS_ON) it will show is on.
If you're using Java 7, you can also choose the middle road here, and do a switch statement with Strings:
switch (input) {
case "is on":
// do stuff
break;
case "is not on":
// etc
}
You're not really breaking the concept up enough, both solutions are brittle...
Look at your syntax
"is", can remove, seems to be ubiquitous
"not", optional, apply a ! to the output comparison
on, before, after, apply comparisons.
So do a split between spaces. Parse the split words to ensure they exist in the syntax definition and then do a step-by-step evaluation of the expression passed in. This will allow you to easily extend the syntax (without having to add an "is" and "is not" for each combination and keep your code easy to read.
Having multiple conditions munged into one for the purposes of switch statements leads to huge bloat over time.
Thanks for the suggestions. They guided me here.
This is almost same as other answers, just a bit simplified.
To summarize, I need to compare the input string with a set of 20 strings and
if they match, do something. Else, do something else.
Static set of strings to which input needs to be compared :
is on,is not on,is before,is after, etc 20 entries
I created an enum
public enum dateRules
{
is_on
,is_not_on
,is_before
,is_after
.
.
.
}
and switching on formatted value of input
if(isARule(in = input.replace(" ","_"))
{
switch(dateRule.valueOf(in))
{
case is_on,
case is_not_on,
case is_before, ...
}
}
I copied the formatted value of 'input' to 'in' so that I can reuse input without another replace of '_' with ' '.
private static boolean isARule(String value)
{
for(dateRule rule : dateRule.values())
{
if(rule.toString().equals(value))
{
return true;
}
}
return false;
}
Problem solved.
Reference : https://stackoverflow.com/a/4936895/1297564
Related
In my Java "Pear" class, I have a huge list of approximately 1000 variables :
public class Pear {
private String
a100,
a110,
a120,
...
etc.
}
I need to set each one of these variables based on the given value of a banana, so I had a first basic idea to do it with a switch case :
public class Pear {
...
public void setValues(Banana aBanana) {
switch (aBanana.getValueName()) {
case "300886":
a100 = aBanana.getValue();
break;
case "309606":
a110 = aBanana.getValue();
break;
case "300843":
a120 = aBanana.getValue();
break;
/* ...and so on for 1000 variables*/
}
}
}
, but I feel like this is not the good way to accomplish this, and this is not going to be very readable neither maintainable. How could I replace this switch case ?
Edit : I think there is a misunderstanding on the call of "setValues". It is going to be called like this (I added some pears) :
public static void main(String[] bananas) {
Pear pear = new Pear();
pear.setValues(bananas[0]);
pear.setValues(bananas[1]);
pear.setValues(bananas[2]);
...etc for 200 times approximately...
}
Having hundreds of variables to store multiple values of the same kind is room for bugs and difficult maintenance (which led to this question).
If you changed your data structure, you would get rid of all the unnecessary variable declarations, and you would have logic coded against values (codes), rather than variable names.
A Map is designed to be used to associate keys to values (value names to values, in your case).
Map<String, String> valueMap = new HashMap<>();
public void setValues(Banana aBanana) {
valueMap.put(aBanana.getValueName(), aBanana.getValue());
}
Now this introduces changes elsewhere, but then that's justified because your typical "read" code would start from the same "value names" too:
public String getValue(String valueName) {
return this.valueMap.get(valueName);
}
Say for instance I have this:
string myString = "hello";
switch (myString) {
case "hello":
break;
case "goodbye":
break;
}
Now I want a method or piece of code, that will retrieve the value of each case declared.
For example, I want to output "hello" and "goodbye" is there a way to get these values?
I was looking for something like this (HYPOTHETICAL):
foreach (case in switch(myString)){
System.out.println("")
}
//which I want output to look like this:
System.out.println("hello")
System.out.println("goodbye")
as a result of the foreach loop. Is something like this possible?
Thanks,
In C# (the answer for Java could be quite different…IMHO it's bad form to have used both tags):
There is no practical way to do what you want. You'd have to reimplement something like dotPeek's or Reflector's functionality, getting the IL for the method and decompiling it to recover the switch statement cases.
However, note that a switch statement can be implemented as a dictionary-based dispatch of delegates. If you do that, then you can just enumerate the keys of your dictionary.
For example:
Dictionary<string, Action> switchStatement = new Dictionary<string, Action>
{
{ "hello", (Action)MyHelloAction },
{ "goodbye", (Action)MyGoodbyeAction },
};
void MyHelloAction() { /* ... */ }
void MyGoodbyeAction() { /* ... */ }
Then you can simply do something like:
foreach (string switchCase in switchStatement.Keys)
{
Console.WriteLine(switchCase);
}
Actually using the switchStatement would look like:
string myString = "hello";
switchStatement[myString]();
In C#, there are 2 reasonably easy solutions to the problem , both using Dictionary-based dispatching. The first is to use a delegate (see Peter Duniho's answer for details). The second would be:
interface Dispatcher
{
void Action();
}
class HelloDispatcher:Dispatcher
{
internal void Action()
{
//place code here
}
}
class GoodbyDispatcher:Dispatcher
{
internal void Action()
{
//place code here
}
}
Then, use Dictionary<string, Dispatcher> type to hold the commands.
In Java, you can only use the second solution; I believe you would want a TreeMap there, although I'm not completely sure.
You can do it without much effort.
First of all, remove the break statement in all cases.
Second Print all case values inside all cases. Your code must be looking like.
public function print(String text){
System.out.println(text);
}
switch (myString)
{
case "hello":
print("hello");
case "goodbye":
print("goodbye");
}
I am trying to make a custom filter in Lucene which simply recognizes whether two consequent words in a text start with a capital letter and have the rest as lower case, in which case the two words are to be joined as one token.
The overriden incrementToken method has the following code
#Override
public boolean incrementToken() throws IOException {
if(!input.incrementToken()){
return false;}
//Case were the previous token WAS NOT starting with capital letter and the rest small
if(previousTokenCanditateMainName==false)
{
if(CheckIfMainName(termAtt.term()))
{
previousTokenCanditateMainName=true;
tempString=this.termAtt.term() ; /*This is the*/
// myToken.offsetAtt=this.offsetAtt; /*Token i need to "delete"*/
tempStartOffset=this.offsetAtt.startOffset();
tempEndOffset=this.offsetAtt.endOffset();
return true;
}
else
{
return true;
}
}
//Case were the previous token WAS a Proper name (starting with Capital and continuiing with small letters)
else
{
if(CheckIfMainName(termAtt.term()))
{
previousTokenCanditateMainName=false;
posIncrAtt.setPositionIncrement(0);
termAtt.setTermBuffer(tempString+TOKEN_SEPARATOR+this.termAtt.term());
offsetAtt.setOffset(tempStartOffset, this.offsetAtt.endOffset());
return true;
}
else
{
previousTokenCanditateMainName=false;
return true;
}
}
}
My question is how once i find the first Token that meets my requirements can i "ignore" it.
Currently the code works perfectly with joining the two tokens but i also get an extra token with the first one of the two that I identified.
I tried using the same method setEnableIncrementsPosition(true) as does the built-in stopFilter but in that case my filter needs to be a TokenFilter type which does not allow me to override the incrementToken method.
I hope i phrased my problem properly
You might have a custom method:
private void tokenize()
where you do the splitting and the custom joins. The resulting List<String> tokens need to be held as an attribute of the tokenizer.
In the incrementToken method you simply check if this attribute is null and initialize it if necessary.
You also need to add the tokens in the incrementToken() method to the termAttribute
termAttribute.append(tokens.get(tokenIndex));
this includes that your Tokenizer needs to have an attribute like this:
private CharTermAttribute termAttribute = addAttribute(CharTermAttribute.class);
Probably you need also some fine tuning. But thats only a draft on how this can be achieved in a pretty simple way.
Current assignment needs me to write a program to read a file with instructions in a very tiny and basic programming language (behaves a little like FORTRAN) and execute those instructions. Basically it's a simple interpreter for the language I guess. It's completely linear, with statements all being defined in sequence and it only has String and integer variables. There are 8 keywords and 4 arithmetic operators I would need to find and define if they exist within the source file, and each line must start off with one of the reserved words.
A program in this language might look something like this:
#COMMENTS
LET.... (declares variables with values)
INTEGER myINT
STRING myString
CALCULATE...
PRINT
PRINTLN
END
Can I use a switch block instead of if-loops to find and then execute all these? My concern is that switches don't work with Strings in Java 6, which is what I'm supposed to be using, but I don't see how to easily assign various int values so the switch block would work. Thanks in advance for any suggestions and advice!
If your language is so simple that every statement begins in its own line and is identified by one word only, then (as Gray pointed out in another comment) you can split the words in each line, then compare the first word against a map. However, I would suggest, instead of mapping the words to ints and then doing one big switch, to map them into objects instead, like this (suggested by Dave Newton):
interface Directive {
public void execute(String line);
}
class LetDirective implements Directive {
public void execute(String line) { ...handle LET directive here... }
}
...define other directives in the same way...
Then define the map:
private Map<String, Directive> directives = new HashMap<String, Directive>();
directives.put("LET", new LetDirective());
...
Then in your parsing method:
int firstSpace = line.indexOf(' ');
String command = line;
if (firstSpace > 0)
command = line.substring(0, firstSpace);
Directive directive = directives.get(command.toUpperCase());
if (directive != null)
directive.execute(line);
else
...show some error...
Each directive would have to parse the rest of the line on its own and handle it correctly inside its execute() method.
The benefit of this over a switch is that you can handle a larger amount of commands without ending up with one gigantic method, but instead with one smaller method per each command.
If you are talking about converting strings to integers then you could do it with an Java enumerated type:
private enum ReservedWord {
LET,
...
}
// skip blank lines and comments
String[] tokens = codeLine.split(" ");
ReservedWord keyword;
try {
keyword = ReservedWord.valueOf(tokens[0]);
} catch (IllegalArgumentException e) {
// spit out nice syntax error message
}
You could also put the processing of the line inside of the enum as a method if you'd like. You could also do it with a Map:
private final Map<String, Integer> reservedWords = new HashMap<String, Integer>();
private final int RESERVED_WORD_LET 1
...
{
reservedWords.put("LET", RESERVED_WORD_LET);
...
}
// skip blank lines and comments
String[] tokens = codeLine.split(" ");
Integer value = reservedWords.get(tokens[0]);
if (value == null) // handle error... ;
switch (value) {
case 1:
// LET
...
}
I have a simple application that reads data in small strings from large text files and saves them to a database. To actually save each such String, the application calls the following method several (may thousands, or more) times:
setValue(String value)
{
if (!ignore(value))
{
// Save the value in the database
}
}
Currently, I implement the ignore() method by just successively comparing a set of Strings, e.g.
public boolean ignore(String value)
{
if (value.equalsIgnoreCase("Value 1") || (value.equalsIgnoreCase("Value 2"))
{
return true;
}
return false;
}
However, because I need to check against many such "ignorable" values, which will be defined in another part of the code, I need to use a data structure for this check, instead of multiple consecutive if statements.
So, my question is, what would be the fastest data structure from standard Java to to implement this? A HashMap? A Set? Something else?
Initialization time is not an issue, since it will happen statically and once per application invocation.
EDIT: The solutions suggested thus far (including HashSet) appear slower than just using a String[] with all the ignored words and just running "equalsIgnoreCase" against each of these.
Use a HashSet, storing the values in lowercase, and its contains() method, which has better lookup performance than TreeSet (constant-time versus log-time for contains).
Set<String> ignored = new HashSet<String>();
ignored.add("value 1"); // store in lowercase
ignored.add("value 2"); // store in lowercase
public boolean ignore(String value) {
return ignored.contains(value.toLowerCase());
}
Storing the values in lowercase and searching for the lowercased input avoids the hassle of dealing with case during comparison, so you get the full speed of the HashSet implementation and zero collection-related code to write (eg Collator, Comparator etc).
EDITED
Thanks to Jon Skeet for pointing out that certain Turkish characters behave oddly when calling toLowerCase(), but if you're not intending on supporting Turkish input (or perhaps other languages with non-standard case issues) then this approach will work well for you.
In most cases I'd normally start with a HashSet<String> - but as you want case-insensitivity, that makes it slightly harder.
You can try using a TreeSet<Object> using an appropriate Collator for case-insensitivity. For example:
Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.SECONDARY);
TreeSet<Object> set = new TreeSet<Object>(collator);
Note that you can't create a TreeSet<String> as Collator only implements Comparator<Object>.
EDIT: While the above version works with just strings, it may be faster to create a TreeSet<CollationKey>:
Collator collator = Collator.getInstance(Locale.US);
collator.setStrength(Collator.SECONDARY);
TreeSet<CollationKey> set = new TreeSet<CollationKey>();
for (String value : valuesToIgnore) {
set.add(collator.getCollationKey(value));
}
Then:
public boolean ignore(String value)
{
return set.contains(collator.getCollationKey(value));
}
It would be nice to have a way of storing the collation keys for all ignored values but then avoid creating new collation keys when testing, but I don't know of a way of doing that.
Add the words to ignore to a list and just check if the word is in that list.
That makes it dynamically.
If using Java 7 this is a fast way to do it:
public boolean ignore(String value) {
switch(value.toLowerCase()) { // see comment Jon Skeet
case "lowercased_ignore_value1":
case "lowercased_ignore_value2":
// etc
return true;
default:
return false;
}
}
It seems that String[] is slightly better (performance-wise) than the other methods proposed, so I will use that.
It is simply something like this:
public boolean ignore(String value)
{
for (String ignore:IGNORED_VALUES)
{
if (ignore.equalsIgnoreCase(value))
{
return true;
}
return false;
}
The IGNORED_VALUES object is just a String[] with all ignored values in there.