I am writing a Processing script and want to detect whether a key (using the key variable) is one of a set of keys chosen by the user ahead of time (say, in a settings JSON file or some other method). I used a switch/case statement to set this up, but it turns out I cannot use variables as case expressions.
I have used the final keyword to mark them as constants, and this works for those defined as direct variables, but not those stored inside an array.
Is there a way of doing this to avoid either using a long if/elseif statement or splitting the array into multiple variables?
In case that's not clear (pun not intended), this is my current code:
// constants
final char[] teamHotkeys = {'b', 'y', 'p', 'o', 'r'};
final char animationHotkey = ' ';
final char doAllHotkey = 'a';
...
// keyPressed handler
void keyPressed(){
float[] chartArea = {gridSizeHeight * 2, gridSizeHeight * 22, gridSizeWidth * 1, gridSizeWidth * 23};
// check which key has been pressed
switch (key){
case teamHotkeys[0]:
drawBar(0, chartArea);
break;
case teamHotkeys[1]:
drawBar(1, chartArea);
break;
case teamHotkeys[2]:
drawBar(2, chartArea);
break;
case teamHotkeys[3]:
drawBar(3, chartArea);
break;
case teamHotkeys[4]:
drawBar(4, chartArea);
break;
case animationHotkey:
runAnimation();
break;
case doAllHotkey:
showFinalScores();
break;
}
}
The compiler can access animationHotkey and doAllHotkey fine, now they are constants, but it can't access the indices of teamHotkeys[] as only the array is a constant, but its members could technically have changed, but the compiler doesnt' know that they never change.
I would just use if statements. Sometimes the dumbest code is the best. It's the easiest to understand, and once it's written you can put it in a function somewhere and not really look at it very much.
But if you really want to avoid if statements, one approach you might take is to create a HashMap that maps character keys to runnable actions. Here's a very basic example:
HashMap<Character, Runnable> keyRunnableMap = new HashMap<Character, Runnable>();
void setup() {
Runnable aRunnable = new Runnable() {
public void run() {
println("A pressed.");
}
};
Runnable bRunnable = new Runnable() {
public void run() {
println("B pressed.");
}
};
keyRunnableMap.put('a', aRunnable);
keyRunnableMap.put('b', bRunnable);
}
void draw() {
}
void keyPressed() {
if (keyRunnableMap.containsKey(key)) {
keyRunnableMap.get(key).run();
}
}
This allows you to keep your keyPressed() logic very short, but it takes more code to set up. You could shorten this a little bit with Java 8 lambdas, but that won't work if you're using the current version of the Processing editor. I don't actually recommend this approach. Just stick with if statements.
Edit: You could also rely on the fact that behind the scenes, the key variable is a char type, which is actually a number. Lower-case 'a' is the number 97. More info here. Here's an example:
Runnable[] runnableArray = new Runnable[2];
void setup() {
Runnable aRunnable = new Runnable() {
public void run() {
println("A pressed.");
}
};
Runnable bRunnable = new Runnable() {
public void run() {
println("B pressed.");
}
};
runnableArray[0] = aRunnable;
runnableArray[1] = bRunnable;
}
void draw() {
}
void keyPressed() {
// a = 97, b = 98
if(key >= 'a' && key <= 'b'){
int index = key - 97;
runnableArray[index].run();
}
}
Again, I don't actually recommend this approach, as it's not any more readable than if statements.
Well, in C# a long switch is basically a HashMap, but in Java it is just... many if/else's, so I usually prefer HashMap when I can use it.
If you only want to know if your key is into a group (let's say, "teamHotkeys") you could use a HashMap<Character, String> or HashMap<Character, KeyType> (with KeyType as an enum), to map multiple keys to a same action. It still will be O(n) in the worst case, but since the keys are grouped it should be better than a lot of if/else's.
HashMap<Character, KeyType> keyTypePairs = new HashMap<Character, KeyType>();
switch(keyTypePairs.get(key)) {
case teamHotkeys:
// ...
break;
}
enum KeyType { teamHotkeys, otherType1, otherType2 }
And that's all, it should work if I did understand the question.
EDIT: With a quick test I can say the space ocuppied by the HashMap is between 2.3 and 3.5 kilobytes in the worst case. About 300 entries = null reserved space, 120 (int)keys and KeyType (each enum uses an 4 or 8 byte address depending on the CPU). Is not THAT much, but I'll take it in acount if I was you. Also, I say 300 as an aproximation; I don't have 120 keys so it could be much less or much more.
Related
wanted to use a switch statement in Java, but the variable in the switch-statement (switch(variable)) should be compared to random numbers in the case-statements (case(randomNumber)). But I get an error message like that: "case expressions must be constant expressions". So does my case-statements doesn't work with random numbers? I thought that the error message means that i have to preface the variables with "final", but that didn't work either. So, are if ... else statements the only way? I hope I explained my problem well enough to understand.
(I'm just beginning with java and the whole terminology that comes with coding)
int number = 6; //some number between 0-9
int randomNumber1 = ((int)(Math.random() * 10));
int randomNumber2 = ((int)(Math.random() * 10));
int randomNumber3 = ((int)(Math.random() * 10));
switch(number)
{
case(randomNumber1):
//some code here
case(randomNumber2):
//some code here
case(randomNumber3):
//some code here
default:
//some code here
}
The reason that it is not working is because you are doing
case(variable)
When I changed this to an actual number, it started working for me. It is saying constant expression required because it wants an actual number, not a number stored in a variable.
Hope this helps
As explained, the (expression) in a case (expression): statement must be a constant.
As for if-else being the only way, another possibility (depending on what you're trying to do overall) is to use the random numbers as keys in a map, with values of objects with a method that can be executed to perform the action you want.
Interface MyAction
{
public void doMyAction() {};
}
public class Eight implements MyAction
{
public void doMyAction() { System.out.println("I'm an eight"); }
}
public class Four implements MyAction
{
public void doMyAction() { System.out.println("I'm a four"); }
}
// ...
HashMap<Integer, MyAction> actionMap = new HashMap<>();
actionMap.put(8, new Eight());
actionMap.put(4, new Four());
// ...
MyAction myAction = actionMap.get(randomNumber);
if (myAction == null) { System.out.println("Don't have one of those"); }
else { myAction.doMyAction(); }
It's hard to see what overall goal you're trying to accomplish, which may be why you're not getting more response -- people here often don't like proposing something that may turn out to be 'wrong', even for a situation that wasn't explained completely.
As seen from the error you are getting, switch can only be used with constant expressions. To achieve what you want, you can only use branching with if, else if, and else.
if(number == randomNumber1){
//some code here
} else if(number == randomNumber2){
//some code here
} else if(number == randomNumber3){
//some code here
} else {
//some code here
}
I want to know if there is any way i could optimize this code.
String[] array;
for(String s:array){
if(s.contains("one"))
//call first function
else if(s.contains("two"))
//call second function
...and so on
}
The string is basically lines I am reading from a file.So there can be many number of lines.And I have to look for specific keywords in those lines and call the corresponding function.
This wont stop you code from doing many String#contains calls, however, it will avoid the if/else chaining..
You can create a key-function map and then iterate over the entries of this map to find which method to call.
public void one() {...}
public void two() {...}
private final Map<String, Runnable> lookup = new HashMap<String, Runnable>() {{
put("one", this::one);
put("two", this::two);
}};
You can then iterate over the entry-set:
for(final String s : array) {
for(final Map.Entry<String, Runnable> entry : lookup) {
if (s.contains(entry.getKey())) {
entry.getValue().run();
break;
}
}
}
You can use switch, but in this case i think the if else is the best way
Since you stated that the order of the checks is not important, you can use a combination of regular expression matching and switch:
static final Pattern KEYWORDS=Pattern.compile("one|two|tree|etc");
Matcher m=KEYWORDS.matcher("");
for(String s:array) {
if(m.reset(s).find()) switch(m.group()) {
case "one": //call first function
break;
case "two": //call second function
break;
case "three": //call third function
break;
case "etc": // etc
break;
}
}
Since this will stop at the first match, regardless of which keyword, it is potentially more efficient than checking one keyword after another, for strings containing a match close to the beginning.
I'm beginner at java, and I'm making a simple program where I type in something, and if what I type in matches one of the things on the "database" then it'll print some text. Is there a simpler way to check this rather than doing this:
int 1;
int 2;
int 3;
etc.
if([USER INPUT].equals("1")) {
System.out.println("TEST");
}
400 times.
Use a switch statement or a HashMap.
Switch statement: Readable, but compiles similarly (if not identically) to an if-else chain.
switch([USER_INPUT]) {
case 1:
System.out.println("TEST");
break;
case 2:
System.out.println("HELLO");
break;
// And so on.
}
Hash Map: Much more readable and simpler. This is preferred.
// Initialization.
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"TEST");
map.put(2,"HELLO");
// Printing.
String s = map.get(USER_INPUT);
if (s == null)
System.out.println("Key doesn't exist.");
System.out.println(s);
Use a HashMap, with key as Integer, and value as text.
System.out.println(myMap.get(USER_INPUT));
Where you have done myMap.put(1, "TEST"); etc, this keeps your code much OO.
the underlying bytecode of switch and if are very comparable, and personally don't see any advantage of switching to switch (unless you want fall through, which means don't include break statement).
A fun alternative would be to use an enum. This would work if you want to define all of the values in a class. It would simplify the code used to get the text value. And it gives you some more fun options beyond what a switch statement would give you.
enum NumberText {
HELLO(1),
WORLD(2);
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
NumberText(Integer pCode) {
this.code = pCode;
}
Static ConnectionGenerator getTextFor(Integer code) {
return map.get(code);
}
}
Then to get the text, simply do this:
NumberText nt = NumberText.getTextFor(USER_INPUT);
System.out.println(nt);
You can get fancier and put an additional constructor variable into the enum and have a specific string of text.
enum NumberText {
HELLO(1, "Hello to You"),
GOODBYE(2, "Goodbye");
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
String text;
NumberText(Integer pCode, String pText) {
this.code = pCode;
this.text = pText;
}
ConnectionGenerator getNumberTextFor(Integer code) {
return map.get(code);
}
getText() {
return this.text;
}
}
Then you could get the text like this:
NumberText.getNumberTextFor(USER_INPUT).getText();
Use a switch statement.
switch(i){
case 1:
System.out.println("Hi");
break;
case 2:
System.out.println("Ok");
break;
// ...
}
You can use a switch statement.
Here's a quick tutorial and some more in-depth explanation.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
The old way, if we wanted to switch on some complicated bitmask, we could easily do it like this (a random example from the top of my head just to demonstrate the issue):
private static final int MAN = 0x00000001;
private static final int WOMAN = 0x00000002;
// ...alive, hungry, blind, etc.
private static final int DEAD = 0xFF000000;
public void doStuff(int human) {
switch (human) {
case MAN | DEAD:
// do something
break;
// more common cases
}
}
Nowadays, since we use enums and EnumSets, I'd sometimes like to do a similar thing:
enum Human {
MAN, WOMAN, DEAD; // etc.
}
public void doStuff(EnumSet human) {
switch (human) {
case Human.MAN | Human.DEAD:
// do something
break;
// more common cases
}
}
which doesn't work, because we can only switch on an int, enum or String value. At this point, I realized it can't be done, even though that enum values are basically just hidden integers. But I like to dig around and the feature looks very useful, so:
private static final EnumSet<Human> DEAD_MAN = EnumSet.of(Human.MAN, Human.DEAD);
public void doStuff(EnumSet human) {
switch (human) {
case DEAD_MAN:
// do something
break;
// more common cases
}
}
Still no luck. Knowing the trick for switch on Strings and that EnumSets are actually 64-bit fields (or arrays of them), I would also try:
switch (human.hashCode()) {
case (Human.MAN.hashCode() | Human.DEAD.hashCode()):
// do something
break;
// more common cases
}
thinking that when the Human hashCode() would be properly implemented to give consistent results, it could work. Nope:
java.lang.Error: Unresolved compilation problem: case expressions must be constant expressions
Now, I wonder why there's no possibility to do this. I always thought of enums and EnumSets in Java like a proper replacement for those old-school bitfields, but here it seems that the new ways can't handle more complicated cases.
The right solution kind of sucks compared to any of the switch possibilities:
public void doStuff(EnumSet human) {
if (human.contains(Human.MAN) && human.contains(Human.DEAD)) {
// do something
} else {
// more common cases
}
}
In particular, since the introduction of switch on Strings, I believe there are at least two possible implementations of switch on EnumSets:
In the case (Human.MAN | Human.DEAD) expressions, simple use a compile-time type check and ordinal() instead of the enums themselves.
Using the same trick as for Strings.
At compile time, compute the hashCode() of the name of the enum values (and possibly something additional - the number of values in enum, the ordinal() etc. - everything is static and constant from the compile time on). Yes, this would mean to change the hashCode() either of the EnumSet class or the Enum class.
use instead of the enums themselves
Now, is there any serious obstacle I didn't take into count (I can come up with a few, all can be easily overcame) that would render this impossible to implement easily? Or am I right that this would indeed be possible, but not desirable enough for Oracle to implement it, because it is not used so often?
Also, let me state that this is a purely academic question possibly without a good answer (don't know, I wouldn't ask otherwise). I might make it community wiki if it proves to be unanswerable. However, I couldn't find an answer (or even anyone discussing it) anywhere, so here it goes.
In Java & Object Oriented world you would have class with setters and getters on an Object and you would use those
public void doStuff(Human human) {
if(human.isDead()) {
if(human.isMale()) {
// something
} else if (human.isFemale()) {
// something else
} else {
// neither
}
}
}
Note: switch is not a good idea because it only takes exact matches. e.g. case MAN | DEAD: will not match MAN | HUNGRY | DEAD unless you only want to match those who were not hungry before they died. ;)
I will see your "absolutely sufficient" benchmark and raise you another flawed benchmark which "shows" it takes a fraction of a clock cycle (in cause you are wondering, that is hard to believe)
public static void main(String... args) {
Human human = new Human();
human.setMale(true);
human.setDead(true);
for(int i=0;i<5;i++) {
long start = System.nanoTime();
int runs = 100000000;
for(int j=0;j< runs;j++)
doStuff(human);
long time = System.nanoTime() - start;
System.out.printf("The average time to doStuff was %.3f ns%n", (double) time / runs);
}
}
public static void doStuff(Human human) {
if (human.isDead()) {
if (human.isMale()) {
// something
} else if (human.isFemale()) {
// something else
} else {
// neither
}
}
}
static class Human {
private boolean dead;
private boolean male;
private boolean female;
public boolean isDead() {
return dead;
}
public boolean isMale() {
return male;
}
public boolean isFemale() {
return female;
}
public void setDead(boolean dead) {
this.dead = dead;
}
public void setMale(boolean male) {
this.male = male;
}
public void setFemale(boolean female) {
this.female = female;
}
}
prints
The average time to doStuff was 0.031 ns
The average time to doStuff was 0.026 ns
The average time to doStuff was 0.000 ns
The average time to doStuff was 0.000 ns
The average time to doStuff was 0.000 ns
Thats 0.1 clock cycles on my machine, before it is optimised away completely.
How about using Set methods of EnumSet.
private static final EnumSet<Human> DEAD_MAN =
EnumSet.of(Human.MAN, Human.DEAD);
public void doStuff(EnumSet human) {
if ( human.containsAll( DEAD_MAN ) )
{
// do something
break;
}
else
{
// more common cases
}
}
Acutally EnumSet's implementation of Set interface methods is very efficient and underneath is the bitfield comparison that you are looking for.
Do the following (based on your example):
enum Human {
MAN, WOMAN, DEAD; // etc.
}
public void doStuff(Human human) {
switch (human) {
case MAN:
case DEAD:
// do something
break;
// more common cases
}
}
If you want EnumSet's then you can't use switch and should refactor it to if
public void doStuff(EnumSet<Human> human) {
if( human.containsAll(EnumSet.<Human>of(Human.MAN, Human.DEAD) {
// do something
}
}
latter variant will do bitwise comparison internally.
public static GetRandomFunc() {
switch((int)(Math.random()*NUM_FUNCTIONS) {
case 0:
functionA();
break;
case 1:
functionB();
break;
case 2:
functionC();
break;
// ...
}
}
I want to call GetRandomFunc() in main randomly until each function has been called once and then it ends. How do I make sure a function would be called once only, and not called again.
It would be easier to store values in an collection, and draw them out randomly until the collection is empty. Or better yet, shuffle the collection and then walk over it sequentially.
The values in the collection could be integers, or they could be objects of different classes (with a common superclass or interface), which provide different implementations of a call() method.
For example:
import java.util.*;
import java.util.concurrent.*;
List<Runnable> functions = new ArrayList<Runnable>();
functions.add(new Runnable() {
public void run() { /* do something */ }
});
functions.add(new Runnable() {
public void run() { /* do something else */ }
});
Collections.shuffle(functions);
for(Runnable function : functions)
function.run();
The other posts on this thread show other potential solutions, but all of them are more complex and error-prone than this one, and most of them would be very slow if the number of functions is large. (The one from #AndersLinden is an exception -- it would still be fast even if there are thousands of functions to call.)
Bit fields to record whether a function has been called and an if statement to not call the function if it's bit is already set - keep looping until all of the bits are set.
(or as Alex D says, create a collection of numbers to use up front)
Either way the trick is to make sure you generate each number once and once only - if you screw this bit up you can end up in an infinite loop (e.g. waiting to get numbers 1, 2 and 3, but your random function is generating 0, 1 and 2)
You could create an array to keep track of which functions have already been used.
For example:
boolean[] usedFunctions = new boolean[NUM_FUNCTIONS];
public static GetRandomFunc() {
switch((int) (Math.random() * NUM_FUNCTIONS) {
case 0:
if(!usedFunctions[0]) {
functionA();
usedFunctions[0] = true;
}
break;
case 1:
if(!usedFunctions[1]) {
functionB();
usedFunctions[1] = true;
}
break;
// etc.
}
}
Then all you need to do is repeatedly call GetRandomFunc() until all elements in usedFunctions are true.
You keep an array of integers that tells which indexes that are still not used.
When you have used an integer, you fill it in the hole with the last index in the list and treat it as a list with one item shorter.
int indexes[] = new int[3];
for (int i = 0; i < 3; i++)
indexes[i] = i;
for (int i = 0; i < 3; i++)
{
int index = (int)(Math.random()*(3 - i));
switch (indexes[index])
{
case 0:
functionA();
break;
case 1:
functionB();
break;
case 2:
functionC();
break;
}
indexes[index] = indexes[2 - i];
}