switch using strings

  • Thread starter Dirk Bruere at NeoPax
  • Start date
D

Dirk Bruere at NeoPax

....which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?
 
A

Arne Vajhøj

...which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?

Multiple if statements.

Arne
 
E

Eric Sosman

...which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?

What's your "neatness" criterion? Several possibilities occur
to me; whether they're "neat" or "messy" is for you to decide:

1) Pre-compute the hashes of the Strings you care about, assign
those values to `static final int' constants, and then

switch (theString.hashCode()) {
case HASH_OF_ABC: ...
case HASH_OF_XYZ: ...
...
}

2) Pre-populate a Map<String,Integer>, look up the String in the
Map, and switch on the Integer value.

3) Like (2), but use an enum.

4) Use polymorphism: Write a class for each String value, use
Class.forName(theString) to load it, and call one of its
methods reflectively.

5) Combine (2/3) and (4): Pre-populate a Map<String,Handler>,
look up the String value, and call a method of the associated
Handler object. (Reflection not required.)

6) Use a chain of "if...else if...else if..."

7) Like (6), but begin with a switch(theString.charAt(0)) to
eliminate many possibilities before starting to compare.

8) Like (7), but with subordinate switches on subsequent chars.
See also "trie."

Personally, I'd pick (6) if the number of interesting String
values was small and well-known, (5) if it were large and/or varying.
De gustibus non disputandum est (Latin for "Don't quarrel with Gus.")
 
D

Dirk Bruere at NeoPax

I thought you might say something horrible like that
Unless you can wait for Java 7, which I believe
is supposed to get switch on strings.

Arne

It's Android, so no.
Maybe do a strings equal thing and assign a number to each comparison,
then do a switch on those? Probably not, now I think it through...
 
D

Dirk Bruere at NeoPax

What's your "neatness" criterion? Several possibilities occur
to me; whether they're "neat" or "messy" is for you to decide:

1) Pre-compute the hashes of the Strings you care about, assign
those values to `static final int' constants, and then

switch (theString.hashCode()) {
case HASH_OF_ABC: ...
case HASH_OF_XYZ: ...
...
}

2) Pre-populate a Map<String,Integer>, look up the String in the
Map, and switch on the Integer value.

3) Like (2), but use an enum.

4) Use polymorphism: Write a class for each String value, use
Class.forName(theString) to load it, and call one of its
methods reflectively.

5) Combine (2/3) and (4): Pre-populate a Map<String,Handler>,
look up the String value, and call a method of the associated
Handler object. (Reflection not required.)

6) Use a chain of "if...else if...else if..."

7) Like (6), but begin with a switch(theString.charAt(0)) to
eliminate many possibilities before starting to compare.

8) Like (7), but with subordinate switches on subsequent chars.
See also "trie."

Personally, I'd pick (6) if the number of interesting String
values was small and well-known, (5) if it were large and/or varying.
De gustibus non disputandum est (Latin for "Don't quarrel with Gus.")

Pick the least worse of all the bad options...
Probably 6

if (str1.equal(str2) {doThis(); goto endofcrap;}
if (str1.equal(str3) doThat();...
....
 
M

Martin Gregorie

Pick the least worse of all the bad options... Probably 6

if (str1.equal(str2) {doThis(); goto endofcrap;} if (str1.equal(str3)
doThat();...
...
IMO the gotos are horrid. I'm with Eric, so use (6) and chain the tests:
if (name.equal(CONST1)
doAction1()
else if (name.equal(CONST2)
doAction2()
else....

because its as fast as using gotos and much more elegant. If there are
more than two or three names it may be worth putting the most frequent
names first and if there are more that 20 or so names go for (2) and make
sure you use something a HashMap or TreeMap for the lookup because they
minimise the number of comparisons in a lookup.
 
E

Eric Sosman

...which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?
[...]
6) Use a chain of "if...else if...else if..."
[...]
Pick the least worse of all the bad options...
Probably 6

if (str1.equal(str2) {doThis(); goto endofcrap;}
if (str1.equal(str3) doThat();...
...

I guess you mean `equals'. I guess you've also forgotten that
Java has no `goto'.

One way to fake the goto, if you really want to, is to put the
whole thing inside a dummy loop and use a labelled `break':

endofcrap: while(true) {
if (str1.equals(str2) { doThis(); break endofcrap; }
if (str1.equals(str3) doThat();...
}

This one's nice because the label is where you *don't* go, thus
making the code harder to read. Another way, without labels:

do {
if (str1.equals(str2) { doThis(); continue; }
if (str1.equals(str3) doThat();...
} while(false);

However, no one with a shred of sense would work so very very hard
to be so very very perverse. Except, perhaps, someone who relies on
the code reviewers to teach him how to spell `else' ...
 
L

Lew

Eric said:

Dirk: Number 6 is the not-least-bad of the options.

1) and 2) are somewhat bad because they use int and Integer. These are not
self-documenting nor especially durable over time.

3) works for many cases where the universe of strings is relatively small.

4) works well only with 5): Map <String, Class <? extends Handler>>

5) Map <String, Handler> works really well in a lot of situations.

You have to be careful that the Handler instances either are thread-safe
(sometimes a pain to ensure) or used only locally to the object that owns the map.

The reflective version, Map <String, Class <? extends Handler>>, works best if
implementing classes have a no-arg constructor. Then the only reflection you
need is 'Handler handler = handlers.get( foo ).newInstance()' (with associated
try-catch).

Enums are great for the low-branch-width scenario that'd have a person
consider 'switch' statements.

Use the enum's 'valueOf()' method or a custom 'fromString()' (for example, to
allow case insensitivity) to arrive at the switch value.

You'll require tight control of the strings allowed to reach the decision
point, with the enum approach in particular.
 
A

Arne Vajhøj

I thought you might say something horrible like that

Why is that so horrible.

if(s.equals("x")) {
...
} else if(s.equals("y")) {
...
} else if(s.equals("z")) {
...
} else {
...
}

is simple, readable and most likely fast enough.
It's Android, so no.

They will also go Java 7 syntax at some point in time.

Arne
 
D

Dirk Bruere at NeoPax

On 2/20/2011 4:24 PM, Dirk Bruere at NeoPax wrote:
...which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?
[...]
6) Use a chain of "if...else if...else if..."
[...]
Pick the least worse of all the bad options...
Probably 6

if (str1.equal(str2) {doThis(); goto endofcrap;}
if (str1.equal(str3) doThat();...
...

I guess you mean `equals'. I guess you've also forgotten that
Java has no `goto'.

My mind is still polluted by C, C++ and esp C#
 
D

Dirk Bruere at NeoPax

Dirk: Number 6 is the not-least-bad of the options.

1) and 2) are somewhat bad because they use int and Integer. These are
not self-documenting nor especially durable over time.

3) works for many cases where the universe of strings is relatively small.

4) works well only with 5): Map <String, Class <? extends Handler>>

5) Map <String, Handler> works really well in a lot of situations.

You have to be careful that the Handler instances either are thread-safe
(sometimes a pain to ensure) or used only locally to the object that
owns the map.

The reflective version, Map <String, Class <? extends Handler>>, works
best if implementing classes have a no-arg constructor. Then the only
reflection you need is 'Handler handler = handlers.get( foo
).newInstance()' (with associated try-catch).

Enums are great for the low-branch-width scenario that'd have a person
consider 'switch' statements.

Use the enum's 'valueOf()' method or a custom 'fromString()' (for
example, to allow case insensitivity) to arrive at the switch value.

You'll require tight control of the strings allowed to reach the
decision point, with the enum approach in particular.
Thanks
 
L

Lew

rossum said:
Or use a multiple-exit method:

  int String2MessageIndex(String messName) {
    if (messName.equals("This"))      { return 0; }
    if (messName.equals("That"))      { return 1; }
    if (messName.equals("The other")) { return 2; }
    if (messName.equals("Something")) { return 3; }
    if (messName.equals("Different")) { return 4; }
    // As many more as needed ...

    // Default return value.
    return UNRECOGNIZED_MESSAGE;
  }

Throwing an exception would be an alternative for an unrecognized
message.

That's just another form of the 'if' chain.

Personally I'd still use 'else if' even with the 'return' statements.
 
R

Roedy Green

...which obviously won't work.
I have a series of packets coming in with a header that contains a
message name in text. I need to find a neat way to do a kind of switch
statement on the message name. Any ideas?

I have often wished Java would implement strings on case statements. I
had it in my DOS-based language, Abundance.

The idiom I used most often now is to create an enum.

Then I convert the string to an enum with valueOf or valueOfAlias (a
method I write like alias that also accepts case-insensitive alias for
each enum).

Then you can use the enum in a switch.

The logical way to write valueOfAlias is as a HashMap. I could not
figure out how to allocate a common static HashMap, so I did it by
scanning each of the constants. See
http://mindprod.com/jgloss/enum.html
for sample code. See the workaround suggested by Piotr Kobzda. You
have to put the HashMap in a separate little class to do it properly.
Enums have some quirks because they are just a form of inner class.
Inner classes can't have statics. This has always struck me as an
arbitary restriction, and it comes to bite here.


Enum.valueOf is implemented like this:

public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum const " + enumType +"." + name);
}



enumConstantDirectory appears to be some sort of static Map, but there
is no other mention of it. It seems to just appear out of nothing,
presumably constructed by special magic when the compiler parses an
enum.
--
Roedy Green Canadian Mind Products
http://mindprod.com
Refactor early. If you procrastinate, you will have
even more code to adjust based on the faulty design.
..
 
A

Arne Vajhøj

That's just another form of the 'if' chain.

Personally I'd still use 'else if' even with the 'return' statements.

switch with N cases and default seems to be most accurate translated to
1 if, N-1 else if and 1 if.

Arne
 
T

Tom Anderson

switch with N cases and default seems to be most accurate translated to
1 if, N-1 else if and 1 if.
^^

That if should be an else, i assume.

A colleague suggested today that it would be useful to be able to switch
on classes, a sort of switch-instanceof hybrid. Something like:

Object value;
switch (value.getClass()) {
case String: storeString((String)value); break;
case Double: storeNumber((Double)value); break;
// etc
}

This would usually be something which would be better handled by
polymorphism, but there are times when you can't do that. There would be a
definite risk that introducing it would lure people away from
polymorphism.

It would be particularly nice if the compiler (or IDE, or PMD/FindBugs)
was able to give you a warning when you were missing a case for a possible
subclass. So for:

Payment p;
switch (pg.getClass()) {
case CreditCard: authorizeCreditCard((CreditCard)p); break;
case GiftVoucher: redeemGiftVoucher((GiftVoucher)p); break;
}

It could warn you that you'd forgotten about paying with loyalty points.

While we're on the subject of reforming switch, i'd like to see the back
of fall-through and break. I want switches to look like:

switch(i) {
case 1: doSomething();
case 2: {
doSomething();
doSomethingElse();
}
case 3, 4: doSomeOtherThing();
}

I already use a braces-and-break style:

switch(i) {
case 1: {
doSomething();
} break;
case 2: {
doSomething();
doSomethingElse();
} break;
case 3:
case 4: {
doSomeOtherThing();
}
}

But it's clunky. I don't know how to introduce the reformed syntax in a
backwards-compatible way, though.

tom
 
J

Joshua Cranmer

This would usually be something which would be better handled by
polymorphism, but there are times when you can't do that. There would be
a definite risk that introducing it would lure people away from
polymorphism.

I think if you want to switch on a Class object, you are highly likely
to have a wrong design, since per-class details are being used not in
the definition of the classes. The logic should either be in the classes
themselves, or you should have a neater pattern to access the classes.
If the classes are logically related, you probably ought to do a visitor
pattern (e.g., source code ASTs). In the case of String v. double v.
<something>, handling data internally as a type-tagged system would
allow you to use polymorphism to suit your needs. In the worst case, you
could always use a reflection-based approach to implement true dynamic
dispatch (ew, maybe invokedynamic would neatify that).
It would be particularly nice if the compiler (or IDE, or PMD/FindBugs)
was able to give you a warning when you were missing a case for a
possible subclass. So for:

Payment p;
switch (pg.getClass()) {
case CreditCard: authorizeCreditCard((CreditCard)p); break;
case GiftVoucher: redeemGiftVoucher((GiftVoucher)p); break;
}

It could warn you that you'd forgotten about paying with loyalty points.

Without knowing anything else about that system... that seems horribly
ill-designed.
switch(i) {
case 1: doSomething();
case 2: {
doSomething();
doSomethingElse();
}
case 3, 4: doSomeOtherThing();
}

How nice of you to include support for the case when two cases go to the
same thing. That is by far my biggest (and I think only?) use of
fall-through in switches.
 
T

Tom Anderson

I think if you want to switch on a Class object, you are highly likely
to have a wrong design, since per-class details are being used not in
the definition of the classes. The logic should either be in the classes
themselves,

Almost always, yes. But not quite always.
or you should have a neater pattern to access the classes. If the
classes are logically related, you probably ought to do a visitor
pattern (e.g., source code ASTs).

That's what i'd usually do. But thinking about it, i don't see why it's
preferable to my hypothetical class switch. What's the benefit? How does
using a visitor rather than a switch help me deliver more value to my
customer?
In the case of String v. double v. <something>, handling data internally
as a type-tagged system would allow you to use polymorphism to suit your
needs. In the worst case, you could always use a reflection-based
approach to implement true dynamic dispatch (ew, maybe invokedynamic
would neatify that).

That has got to be the fastest abandonment of the technical high ground i
have ever seen. Did you use a sledge? :)
Without knowing anything else about that system... that seems horribly
ill-designed.

It's mostly a contrived example. I mean, we do actually have some logic
almost exactly like that, but we recognise it's a mistake and are working
on getting rid of it!
How nice of you to include support for the case when two cases go to the
same thing. That is by far my biggest (and I think only?) use of
fall-through in switches.

Duff's device!

tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,125
Latest member
VinayKumar Nevatia_
Top