So I wrote a method today that incorporated the use of nested switch statements, and the code looked fairly clean and concise to me, but I was told that nested switch statements are not typically the best way to go as they can get confusing with the more switch statements that you add on. Here is a sample of what my code looked like:
EnumOne enumOne;
EnumTwo enumTwo = null;
EnumTwo enumThree = null;
switch (enumOne) {
case CASE_ONE:
switch (enumTwo){
case A: enumTwo = EnumTwo.B; break;
case C: enumTwo = EnumTwo.D; break;
default: break;
}
switch (enumThree) {
case AA: enumThree = EnumTwo.BB; break;
case CC: enumThree = EnumTwo.DD; break;
default: break;
}
break;
case CASE_TWO:
case CASE_THREE:
switch(EnumTwo) {
default: break;
}
switch (enumThree) {
case AA: enumThree = EnumTwo.XX; break;
case CC: enumThree = EnumTwo.YY; break;
default: break;
}
break;
default:
break;
}
So my question would be, essentially, what would be a suitable alternative to these switch statements?
As using a lot of switch becomes pretty hard to read.
And any time a new case arises then we have to modify code and add a CASE
we can consider using polymorphism in such cases
I am going to give a simple class just to let you understand.
Suppose a class earlier with switch case
class Test
{
Animal a;
public Test(Animal a)
{
this.a=a;
}
public moveThisAnimal()
{
switch(this.a)
{
case fish:
System.out.println("swim");
break;
case dog:
System.out.println("walk");
break;
case bird:
System.out.println("fly");
break;
}
}
}
now we replace these switch with our polymorphism logic
Interface Animal
{
String move();
}
Class Dog implements Animal
{
public String move()
{
return "walk";
}
}
Class Bird implements Animal
{
public String move()
{
return "fly";
}
}
Class Fish implements Animal
{
public String move()
{
return "swim";
}
}
now we have Test class without switch case
class Test
{
Animal a;
public Test(Animal a)
{
this.a=a;
}
public moveThisAnimal()
{
System.out.println(this.a.move()); // all switch case statements removed
}
}
and even if we have to add further cases we have to just add implementations no change here
See your complete code and see if It is possible to Do
I recommend you replace each nested switch statement with a call to a procedure which then executes the nested switch code.
Write something like this instead:
EnumOne enumOne;
EnumTwo enumTwo = null;
EnumTwo enumThree = null;
switch (enumOne)
{
case CASE_ONE:
nested_switch1();
case CASE_TWO:
case CASE_THREE:
nested_switch2();
break;
default:
break;
}
nested_switch1() {
switch (enumTwo)
{
case A:
enumTwo = EnumTwo.B;
break;
case C:
enumTwo = EnumTwo.D;
break;
default:
break;
}
switch (enumThree)
{
case AA:
enumTwo = EnumTwo.BB;
break;
case CC:
enumTwo = EnumTwo.DD;
break;
default:
break;
}
break;
}
nested_switch2() {
switch(EnumTwo)
{
default:
break;
}
switch (enumThree)
{
case AA:
enumTwo = EnumTwo.XX;
break;
case CC:
enumTwo = EnumTwo.YY;
break;
default:
break;
}
}
If you have integers X and Y and you need to switch on both, you can combine them in some unambiguous way and switch on the combination. For example, if y < 10:
switch (x*10+y)
{
case 0: // x == y == 0
case 1: // x ==0, y == 1
///
case 10: // x == 1, y == 0
case 11: // x == y == 1
//
}
Related
I have the function below and I want to add synonyms to each case for example case "north" could also be "up", case "east" could be right etc. Since all the cases fall into the same try/catch statement I wasn't sure how I could do that for each specific case. Appreciate any help! Thanks
public void changeRoom(boolean isValidInput, String[] input, int attemptCount) {
while (isValidInput) {
switch (input[1]) {
case "north":
case "east":
case "south":
case "west":
try {
if (world.getCurrentRoom().roomExits.containsKey(input[1])) {
player.setMostRecentExit(input[1]);
world.setCurrentRoom(world.getCurrentRoom().roomExits.get(input[1]));
isValidInput = false;
if (isSound) {
walkEffect.playSoundEffect();
}
Thread.sleep(1800);
narrateRooms(world.getCurrentRoom().getDescription());
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
default:
System.out.println("You hit a wall. Try again: ");
System.out.print(">>>");
attemptCount++;
if (attemptCount >= 2) {
System.out.println();
openMap();
System.out.println("Where would you like to go? ");
System.out.print(">>>");
}
input = scanner.nextLine().strip().toLowerCase().split(" ");
break;
}
}
I was able to normalize the input by creating the function below. Thanks, everybody!
public String normalizeText(String input) {
List<String> northOptions = Arrays.asList("north", "up");
List<String> southOptions = Arrays.asList("south", "down");
List<String> eastOptions = Arrays.asList("east", "right");
List<String> westOptions = Arrays.asList("west", "left");
if (northOptions.contains(input.toLowerCase())) {
return "north";
}
if (southOptions.contains(input.toLowerCase())) {
return "south";
}
if (eastOptions.contains(input.toLowerCase())) {
return "east";
}
if (westOptions.contains(input.toLowerCase())) {
return "west";
}
return "";
}
Just list those cases like that:
switch (input[1]) {
case "north": case "up":
case "east": case "right":
case "south": case "down":
case "west": case "left":
I created method like this, where I have 2 inputs.
first is type (e.g. 'd', 'h')
second is value (e.g. "15", "0.5")
I created function to convert it to minutes like this:
public Float toMinutes(char type, String value) {
Float minutes = 0f;
switch (type) {
case 'd': {
minutes += Integer.parseInt(value) * 1440;
break;
}
case 'h': {
minutes += Float.parseFloat(value) * 60;
break;
}
case 'm': {
minutes += Integer.parseInt(value);
break;
}
default: {
return 0f;
}
}
return minutes;
}
I decided to refactor it, because those multiplication looks "ugly" for me. I found a better solution called TimeUnit.
public Long toMinutes(char type, String value) {
Long minutesCounted = 0l;
TimeUnit tu = TimeUnit.MINUTES;
switch (type) {
case 'd': {
minutesCounted += tu.convert(Long.parseLong(value), TimeUnit.DAYS);
break;
}
case 'h': {
minutesCounted += tu.convert(Long.parseLong(value), TimeUnit.HOURS);
break;
}
case 'm': {
minutesCounted += tu.convert(Long.parseLong(value), TimeUnit.MINUTES);
break;
}
default: {
return 0l;
}
}
return minutesCounted;
}
The problem is that this converter allow only long values, so now it works only on inputs like 15h and it will not work on inputs like 1,5h. Any ideas how to improve my solution to work with floating numbers?
Instead of using magic constants, you could use TimeUnit to figure out the conversion rate for 1 d, h, etc. to minutes like this
public float toMinutes(char type, String value) {
switch (type) {
case 'd':
return Integer.parseInt(value) * TimeUnit.DAYS.toMinutes(1);
case 'h':
return Float.parseFloat(value) * TimeUnit.HOURS.toMinutes(1);
case 'm':
return Integer.parseInt(value);
default:
return 0;
}
}
I am new to Java, would appreciate some help.
what I am trying to do here is switch between the cases. However what I also need is to not skip the remaining cases in this procedure.
I mean the code below should output:
two
four
three
four
public class X {
public static void main(String[] args) {
String n = "two";
while(true)
{
switch (n)
{
case "zero":
{
System.out.println("zero");
n="one";
}
case "one":
{
System.out.println("one");
n="three";
}
case "two":
{
System.out.println("two");
if (12>3)
{
n="four";
break;
}
}
case "three":
{
System.out.println("three");
}
case "four":
{
System.out.println("four");
return;
}
}
}
}
}
I have used return to end the while loop. Now I do understand that I have used break in case two which will break the current switch and thereby not complete it. But if i don't, I just can't switch between cases in the first place. So, I need a different solution. And working implementation in java should be helpful. Thanks
You can use Queue for this.
Each case is responsible to add the next one to the queue by the order of expected execution.
Queue q = new LinkedList();
q.add("two");
String n = (String)q.poll();
while(n!=null)
{
switch (n)
{
case "zero":
{
System.out.println("zero");
q.add("one");
break;
}
case "one":
{
System.out.println("one");
q.add("three");
q.add("two");
break;
}
case "two":
{
System.out.println("two");
if (12>3)
{
q.add("four");
}
q.add("three");
break;
}
case "three":
{
System.out.println("three");
q.add("four");
break;
}
case "four":
{
System.out.println("four");
break;
}
}
n=(String) q.poll();
}
The problem does not lie within the switch statement. You simply need to distinguish from where you 'came from'.
To do that you need to remember it, e.g. in a local variable:
String lastN;
and in the loop before the switch:
lastN = n;
Now you can do something like:
case "four":
{
System.out.println("four");
if("three".equals(lastN))
{
return;
}
else
{
n = "three";
break;
}
}
Your code is working as it should, if what you need is a fall through
then consider the fact that case zero and one must be executed first...
on the other hand, you don't need to add { } to the case group
case "zero": {
System.out.println("zero");
n = "one";
}
public static void main(String[] args) {
String n = "two";
while(true)
{
switch (n)
{
case "zero":
{
System.out.println("zero");
}
case "one":
{
System.out.println("one");
}
case "two":
{
System.out.println("two");
}
case "four":
{
System.out.println("four");
if(n=="four"){
return;
}
}
case "three":
{
System.out.println("three");
n="four";
}
}
}
}
This question already has answers here:
Handling parenthesis while converting infix expressions to postfix expressions
(2 answers)
Closed 5 years ago.
I've been working on a school assignment that requires us to convert a string from infix notation to postfix notation. We have to do this by using a stack implementing an array.
Here is my code for the actual conversion class:
package practice;
public class Practice {
public static String infixToPostfix(String infix)
{
Practice2 operatorStack = new Practice2();
String output = "";
char curChar;
String input = infix;
boolean isOperator=false;
boolean empty=true;
int curCharPrecedence=0;
int topOfStackPrecedence=0;
for(int i =0; i<input.length();i++){
curChar = input.charAt(i);
switch(curChar){
case '+': isOperator=true;
case '-': isOperator=true;
case '/': isOperator=true;
case '*': isOperator=true;
case '(': isOperator=true;
case ')': isOperator=true;
break;
default: isOperator=false;
}
if(!isOperator){
output=output+curChar;
}
else if(isOperator){
output+=" ";
if(empty){
empty=false;
operatorStack.Push(curChar);
}
else if(!operatorStack.empty()){
switch(curChar){
case ')': topOfStackPrecedence=0;
case '+': curCharPrecedence=1;
case '-': curCharPrecedence=1;
case '/': curCharPrecedence=2;
case '*': curCharPrecedence=2;
case '(': topOfStackPrecedence=3;
}
switch((Character) operatorStack.peek()){
case ')': topOfStackPrecedence=0;
case '+': topOfStackPrecedence=1;
case '-': topOfStackPrecedence=1;
case '/': topOfStackPrecedence=2;
case '*': topOfStackPrecedence=2;
case '(': topOfStackPrecedence=3;
}
if(curCharPrecedence>topOfStackPrecedence){
operatorStack.Push(curChar);
}
else{
while(!operatorStack.empty()&&topOfStackPrecedence>curCharPrecedence){
output+= operatorStack.pop();
output+=" ";
if(!operatorStack.empty())
switch((Character) operatorStack.peek()){
case ')': topOfStackPrecedence=0;
case '+': topOfStackPrecedence=1;
case '-': topOfStackPrecedence=1;
case '/': topOfStackPrecedence=2;
case '*': topOfStackPrecedence=2;
case '(': topOfStackPrecedence=3;
}
}
operatorStack.Push(curChar);
}
}
}
}
while(!operatorStack.empty()){
output+=" ";
output+= operatorStack.pop();
}
return output;
}
public static void main(String[] args)
{
System.out.println(infixToPostfix("a+b*c"));
}
}
Then here is my code for the stack class:
package practice;
import java.util.EmptyStackException;
public class Practice2<T> extends Object{
public T[] stack = (T[]) new Object[10];
int topOfStack =-1;
public Practice2()
{
stack = (T[]) new Object[10];
}
public Practice2(int capacity)
{
stack = (T[]) new Object[capacity];
}
public T Push(Object item)
{
if(topOfStack<=stack.length-1){
T[] temporary = (T[]) new Object[stack.length];
for(int i=0; i<stack.length-1;i++){
temporary[i]=stack[i];
}
stack = (T[]) new Object[temporary.length+10];
for(int i=0; i<temporary.length-1;i++){
stack[i]=temporary[i];
}
}
topOfStack++;
stack[topOfStack]= (T) item;
return (T) item;
}
public T peek()
{
return stack[topOfStack];
}
public T pop()
{
if(topOfStack==-1){
throw new EmptyStackException();
}
else
return stack[topOfStack--];
}
public boolean empty()
{
if(topOfStack==-1)
return true;
else
return false;
}
public int size(){
return topOfStack+1;
}
}
Whenever I try to run this I get the following error message:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at practice.Practice2.peek(Practice2.java:49)
at practice.Practice.infixToPostfix(Practice.java:53)
at practice.Practice.main(Practice.java:106
It appears that the problem is my peek method in the stack class, but I'm not entirely sure.
Any help is greatly appreciated, I've spent way to much time on this program and badly need someone else to take a look at it.
The error is quite clear ArrayIndexOutOfBoundsException ....
and the reason is here:
switch((Character) operatorStack.peek()){
you are doing a peek before a push... more specific: you are trying to get the element of the array located at the topOfStack =-1 and that is the reason of the exception...
I'm working on a card game app and i finished the basic stuff and now i'm trying to make it look professional.
the first thing I want to do is the effect of the distribution of cards,
i want to make a shuffle card effect.
when a card is given to a player, I want at least 500 milliseconds difference to the next card that will be distributed to him.
ideas?
this is a part from my code..
private void SetTheGame() {
SetShuffleSound();
for ( int i = 0; i < Imagename.length;i++) {
Imagename[i] = (ImageView) findViewById(WTF[i]);
CountCards();
Random = getRandom();
SwitchImages SwitchMe = new SwitchImages(myNewArray[Random]);
int first = SwitchMe.ChangeImages();
Imagename[i].setImageResource(myNewArray[Random]);
Imagename[i].setVisibility(View.VISIBLE);
CardsCount valueOfCard = new CardsCount(myNewArray[Random]);
int a = valueOfCard.WhatsMyValue();
String b = valueOfCard.TheFamily();
switch (i) {
case 0:
if (first != 0) {
Imagename[0].setImageResource(first);
}
FirstColumnComputer.add(a);
FirstColumnComputerFAMILY.add(b);
break;
case 1:
if (first != 0) {
Imagename[1].setImageResource(first);
}
SecondColumnComputer.add(a);
SecondColumnComputerFAMILY.add(b);
break;
case 2:
if (first != 0) {
Imagename[2].setImageResource(first);
}
ThirdColumnComputer.add(a);
ThirdColumnComputerFAMILY.add(b);
break;
case 3:
if (first != 0) {
Imagename[3].setImageResource(first);
}
FourColumnComputer.add(a);
FourColumnComputerFAMILY.add(b);
break;
case 4:
if (first != 0) {
Imagename[4].setImageResource(first);
}
FifthColumnComputer.add(a);
FifthColumnComputerFAMILY.add(b);
break;
case 5:
FirstColumnPlayer.add(a);
FirstColumnPlayerFAMILY.add(b);
break;
case 6:
SecondColumnPlayer.add(a);
SecondColumnPlayerFAMILY.add(b);
break;
case 7:
ThirdColumnPlayer.add(a);
ThirdColumnPlayerFAMILY.add(b);
break;
case 8:
FourColumnPlayer.add(a);
FourColumnPlayerFAMILY.add(b);
break;
case 9:
FifthColumnPlayer.add(a);
FifthColumnPlayerFAMILY.add(b);
break;
}
Cards.remove(Random);
// MakeTheCardPause();
}
SentTheLinkedList();
}
MakeTheCardPause() is the problem...
private void MakeTheCardPause() {
Thread Timer = new Thread()
{
public void run()
{
try{
sleep(1000);
}catch(InterruptedException e)
{
e.printStackTrace();
}finally
{
//do something...
}
}
};
Timer.start();
}
thanks!
Many ways you can do this. Thread.sleep(500) is the way was you suggested but it is not what I would recommend. Here are two alternatives
Message Handler
An example
Handler mHandler = new Handler(){
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what){
case shuffle:
// Do something
break;
case doneShuffle:
//Do something
}
}
};
Asynch Tasks
Here is an example:
private class shuffleCards extends AsyncTask<Card, Integer, Long> {
protected Long doInBackground(Card card) {
//Do something
//shuffle deck
// Escape early if cancel() is called
if (isCancelled()) break;
}
return deck;
}
protected void onProgressUpdate(Integer... progress) {
//Number of shuffled cards??
}
protected void onPostExecute(Long result) {
//Show card
}
}
Remember this is just a background task to display results. Your main thread will be handling the actual card values and handing them over to the Asynch task.
Good Luck
What about this? You need to have the sleep in the working thread, your code above is creating a new thread and telling it to sleep, which has no noticeable effect to the user.
private void SetTheGame() {
SetShuffleSound();
for ( int i = 0; i < Imagename.length;i++) {
Imagename[i] = (ImageView) findViewById(WTF[i]);
CountCards();
Random = getRandom();
SwitchImages SwitchMe = new SwitchImages(myNewArray[Random]);
int first = SwitchMe.ChangeImages();
Imagename[i].setImageResource(myNewArray[Random]);
Imagename[i].setVisibility(View.VISIBLE);
CardsCount valueOfCard = new CardsCount(myNewArray[Random]);
int a = valueOfCard.WhatsMyValue();
String b = valueOfCard.TheFamily();
switch (i) {
case 0:
if (first != 0) {
Imagename[0].setImageResource(first);
}
FirstColumnComputer.add(a);
FirstColumnComputerFAMILY.add(b);
break;
case 1:
if (first != 0) {
Imagename[1].setImageResource(first);
}
SecondColumnComputer.add(a);
SecondColumnComputerFAMILY.add(b);
break;
case 2:
if (first != 0) {
Imagename[2].setImageResource(first);
}
ThirdColumnComputer.add(a);
ThirdColumnComputerFAMILY.add(b);
break;
case 3:
if (first != 0) {
Imagename[3].setImageResource(first);
}
FourColumnComputer.add(a);
FourColumnComputerFAMILY.add(b);
break;
case 4:
if (first != 0) {
Imagename[4].setImageResource(first);
}
FifthColumnComputer.add(a);
FifthColumnComputerFAMILY.add(b);
break;
case 5:
FirstColumnPlayer.add(a);
FirstColumnPlayerFAMILY.add(b);
break;
case 6:
SecondColumnPlayer.add(a);
SecondColumnPlayerFAMILY.add(b);
break;
case 7:
ThirdColumnPlayer.add(a);
ThirdColumnPlayerFAMILY.add(b);
break;
case 8:
FourColumnPlayer.add(a);
FourColumnPlayerFAMILY.add(b);
break;
case 9:
FifthColumnPlayer.add(a);
FifthColumnPlayerFAMILY.add(b);
break;
}
Cards.remove(Random);
long sleepMax = 1000L;
Random r = new Random();
long delay = (long) (r.nextDouble() * range);
Thread.sleep(delay);
}
SentTheLinkedList();
}