Alternative to Switch Case in Java

Alternative to Switch Case in Java

Presumably you're struggling with the requirement of case's being constant. Typically this is a code-smell, but there are things you can do. You might want to raise and link to another question that details why you're trying to switch.

Map<String,Object> map = new HasMap<String,Object>();
// ... insert stuff into map
// eg: map.add("something", new MyObject());

String key = "something";
if (map.contains(key)) {
Object o = map.get(key);
}

In the example above, you might want to map to 'handlers', something like

interface Handler {
public void doSomething();
}

which then makes this all turn into a lookup.

if (map.contains(key)) { map.get(key).doSomething(); }

Again, it's a bit of a smell, so please post a question which illustrates the reasoning.

Performance of arrays as alternative to switch cases

The code you wrote is just bad use of switches.

switches are great when one, or preferably both, of these things holds:

  • The values you are triggering on aren't consecutive (i.e. case 384:, then case 30491:, etcetera. Trivial example is when switching on a string for a certain command.
  • The thing you want to do is a bunch of code (vs. returning a single result, as here).

For what its worth, you've also written the case block in a rather convoluted fashion. If you have a separate method for this, you could write:

switch (foo) {
case 0: return "Sunday";
case 1: return "Monday";
}

Also (new in.. java 14?), you can write this too:

String dayName = switch(foo) {
case 0 -> "Sunday";
case 1 -> "Monday";
default -> throw new IllegalArgumentException("Unknown day");
};

Lastly, this code should not exist. Because java.time.DayOfWeek already exists. You've badly reinvented the wheel.

Performance

When talking about 7 elements it just doesn't matter. Other factors dominate, simple as that.

As a general principle (which doesn't kick in until many many hundreds, maybe thousands, see footnote!), the reason your array-based code is linear as you wrote it is solely because java would not (currently; future java changes are afoot that may change this) realize that the line that 'makes' your string array with day names can be executed once for the lifetime of a JVM boot and never again; in other words, this code, under the hood, compiles down to making a new string array with 7 slots and then assigning 7 constants to the slots (those constants are the references to the strings, which are calculated "once per VM lifetime").

If you move that array into a static field, that goes away and they're both O(1) performance (the same 'speed' whether you have 7 days or 7 million days), because they both just do a single lookup. No looping.

IMPORTANT PERFORMANCE FOOTNOTE: Computers are incredibly complex and act nothing like a von neumann model. Predicting the performance of code based on common sense therefore usually steers you extremely wrong. There are therefore 3 important rules to keep in mind:

  • Write code the way java programmers the world over tend to write it, and keep things flexible. IF performance issues occur, you will probably need to change the relevant part of your app. This is easier if your code is flexible. Most rules of thumb about performance you read about make your code less flexible and are therefore bad! - also, the JVM optimizes code, and does this essentially by being a giant pattern matching machine, detecting patterns it knows how to run well. The programmers who make this pattern matching optimizing machine tend to write patterns that are commonly used in java code. Thus, idiomatic java code tends to be fast in practice.

  • Nobody can look at code and just know how it ends up performing in real life, because it's too complex. Don't take my word for it; the very engineers that write the VM say this all the time. If they can't figure it out, you stand no chance whatsoever. Therefore, a profiler report or JMH timing result is required before even thinking about letting code performance ideas determine how you write your code. See the rule above: Without this, the best, fastest code is the cleanest, most flexible, most easy to read code.

  • The one exception is algorithmic complexity (big O notation). This is a complex topic (generally, university-level informatics and very math heavy), well beyond an SO answer, but plenty of online resources can be found if you want to dive in. But do note that the big-O factor often doesn't start controlling until you get a ton of data. For example, in this case technically your array-based code is O(n) (because you create the array inside the code, every time you run it), whereas the switch is O(1), but given that n is 7 here, that's irrelevant. It probably won't get relevant until you get to hundreds of items.

Is there any alternatives to a huge switch, with multiple cases and fallthrough in JavaScript?

Perhaps an object?

const titles = {  'name':        ['name', 'idea', 'ide', 'ide navn', 'title', 'idea name'],  'description': ['beskrivelse', 'problemet', 'description', 'the problem', 'ide beskrivelse'],  'owner' :      ['ejer', 'owner', 'opfinder', 'ide person', 'idea person', 'person'],  'duedate' :    ['duedate', 'deadline', 'tidsfrist', 'sidste dato', 'dato', 'due date'],  'imageUrl' :   ['billede', 'billeder', 'image', 'images', 'attachment']}const getKey = (obj,val) => Object.keys(obj).find(key => obj[key].indexOf(val) !=-1 );
function findHeader(object) { var title = object.toString().trim().toLowerCase(); return getKey(titles,title) || 'Unassigned' }
console.log( findHeader("Owner"), findHeader("Bla"))

Alternative of if-else and switch statements

If you can use JavaScript, you can use a object with functions:

function doSomething(i) {
var obj = {};

obj[12] = function () {
// order should be same
up();
left();
stop();
};
obj[304] = function () {
// order should be same
right();
up();
stop();
};
obj[962] = function () {
// order should be same
down();
left();
up();
stop();
};

// apparently we can't use any conditional statements
try {
obj[i]();
} catch (e) {}
}

If only if and switch statements aren't allowed, replace all the if statements with the logical AND operator (&&):

function doSomething(i) {
(i == 12) && (
// order should be same
up(),
left(),
stop()
);

(i == 304) && (
// order should be same
right(),
up(),
stop()
);

(i == 962) && (
// order should be same
down(),
left(),
up(),
stop()
);
}

Can somebody recommend a java 8 pattern to replace a switch statement?

when a new type is added to class A the compiler should flag all the switch statements that need to be adjusted?

A good approach to this would be replacing switch statements with a more robust implementation of multiple dispatch, such as the Visitor Pattern:

interface VisitorOfA {
Object visitA(A a);
Object visitB(B b);
}
class A {
Object accept(VisitorOfA visitor) {
return visitor.visitA(this);
}
}
class B extends A {
Object accept(VisitorOfA visitor) {
return visitor.visitB(this);
}
}

With this infrastructure in place, you can remove your switch statements, replacing them with implementations of the visitor:

Object res = a.accept(new VisitorOfA() {
public Object visitA(A a) { return new Bla(); }
public Object visitB(B b) { return new Cop(); }
});

When you add a new subtype to A, say, class C, all you need to do is adding a new method to VisitorOfA:

Object visitC(C c);

Now the compiler will spot all places where this new method has not been implemented, helping you avoid problems at runtime.

Alternative to Nested Switch Statements in Java

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;
}
}


Related Topics



Leave a reply



Submit