Java Forum / GUI / January 2004
ActionEvent Performance Issue
Jim Cobban - 11 Dec 2003 18:46 GMT I am trying to handle ActionEvents spawned by various components in a dialog panel in an efficient way. Perhaps I am missing something but I cannot find an example of how to do this.
The examples I have found use setActionCommand on JButtons or JTextFields to assign an action command String to the component. This String is then passed in the ActionEvent to actionPerformed method of the supplied ActionListener. This method looks something like:
public void actionPerformed(ActionEvent ev) { String actionCommand = ev.getActionCommand(); if (actionCommand == SAVE) { // apply updates to the Register element performSaveAction(); } // apply updates to the Register element else if (actionCommand == SAVE_AS) { // apply updates to the Register element performSaveAsAction(); } // apply updates to the Register element else if (actionCommand == CANCEL) { // finished with dialog frame.doDefaultCloseAction();// close the dialog without updating } // finished with dialog else if (actionCommand == EDIT_FIELD) { // pop up dialog to edit attributes of Field performEditFieldAction(); ... What bugs me about this implementation is that I cannot replace the series of if-then-else statements with a switch statement to branch directly to the function to be performed. That is because the interface for setActionCommand specifies that its parameter must be a String, rather than an Object. If it were an Object then I could pass in an Integer containing the desired case statement value.
The only alternative that I can see is to associate a separate Action with each JButton or JTextField, which I feel is excessive overhead. Using setActionCommand, I need merely implement the ActionListener interface within the class creating the dialog, as opposed to creating a separate class implementing Action corresponding to each component in the panel.
 Signature Jim Cobban jcobban@magma.ca 34 Palomino Dr. Kanata, ON, CANADA K2M 1M1 +1-613-592-9438
Steve W. Jackson - 11 Dec 2003 19:39 GMT >:I am trying to handle ActionEvents spawned by various components in a dialog >:panel in an efficient way. Perhaps I am missing something but I cannot find [quoted text clipped - 39 lines] >:within the class creating the dialog, as opposed to creating a separate >:class implementing Action corresponding to each component in the panel. There are pros and cons to the public nature of the actionPerformed method that's shared by a variety of items, as well as for separate ones for each. So I'll skip that area for this reply.
As to your wish to use a switch, this is the same complaint I've long had with C, which is the basic syntax foundation for Java. But it's probably simple to work around it for your particular use. Simply set the action command string to a number. If you want a 1, use "1". Then, in your actionPerformed code, you can extract it via something like:
int value = Integer.parseInt(ev.getActionCommand());
You'll have to work out the NumberFormatException details to cover a non-numeric value, of course, or other details. But this would allow you to use a switch if you preferred. Of course, judicious arrangement of your else-if alternatives can give you about the same performance in most cases -- YMMV.
= Steve =
 Signature Steve W. Jackson Montgomery, Alabama
Skippy - 12 Dec 2003 10:55 GMT > >:What bugs me about this implementation is that I cannot replace the series > >:of if-then-else statements with a switch statement to branch directly to the > >:function to be performed. That is because the interface for > >:setActionCommand specifies that its parameter must be a String, rather than > >:an Object. If it were an Object then I could pass in an Integer containing > >:the desired case statement value. switch(actionCommand.hashcode()) { case SAVE_AS.hashcode(): [...]; }
just a brainwave
Antti S. Brax - 12 Dec 2003 11:11 GMT a@b.invalid wrote in comp.lang.java.gui:
> switch(actionCommand.hashcode()) > { > case SAVE_AS.hashcode(): [...]; > } > > just a brainwave SAVE_AS.hashcode() is not a constant and therefore can not be used as a value in a case statement.
 Signature Antti S. Brax - asb(at)iki.fi Rullalautailu pitää lapset poissa ladulta http://www.iki.fi/asb/ http://www.cs.helsinki.fi/u/abrax/hlb/
Jim Cobban - 12 Dec 2003 22:36 GMT > a@b.invalid wrote in comp.lang.java.gui: > > switch(actionCommand.hashcode()) [quoted text clipped - 6 lines] > SAVE_AS.hashcode() is not a constant and therefore can not be > used as a value in a case statement. Furthermore hashcode does not map to a small range of integers, and therefore could not be efficiently implemented by javac. If your case values are not fairly contiguous the compiler has to either emit if-then-else code or else construct a hash table.
Alan Moore - 13 Dec 2003 04:50 GMT >I am trying to handle ActionEvents spawned by various components in a dialog >panel in an efficient way. Perhaps I am missing something but I cannot find [quoted text clipped - 39 lines] >within the class creating the dialog, as opposed to creating a separate >class implementing Action corresponding to each component in the panel. You're talking about something that happens when a user clicks on a button; performance is not an issue.
Jim Cobban - 15 Dec 2003 19:15 GMT > You're talking about something that happens when a user clicks on a > button; performance is not an issue. That is a reasonable point. I do not like being forced to write code in an obviously inefficient way even if it is not on a performance critical path. More significantly, perhaps, I find a switch statement to be more self-documenting of the intention of the code. Since the intention is to select one of several more or less equal pieces of code to execute based upon a value, if-then-else is simply not the most desirable construct to employ. For that matter although I personally use a formatting style in which all of the ifs in an if-then-else string are at the same indentation level, that is not the standard convention. If I apply a pretty-formatter to my code or allow my editor to apply standard formatting, the later cases will soon scroll off the screen to the right. Using if-then-else logic forces me to declare that some of the buttons on my panel are more "important" than others.
Perhaps if there was some obvious reason why the argument of setActionCommand is better as a String rather than on Object this wouldn't bug me. But since the actual String value is never used, at least in Localized code, I honestly don't see the point. The only thing that is ever done with the argument is to compare its reference to a reference to one of the defining Strings. That would work regardless of what type of Object it was.
Alan Moore - 15 Dec 2003 20:58 GMT >Perhaps if there was some obvious reason why the argument of >setActionCommand is better as a String rather than on Object this wouldn't [quoted text clipped - 3 lines] >the defining Strings. That would work regardless of what type of Object it >was. Oddly enough, I ran into this issue myself the other day, when I wanted to use a Typesafe Enum as the action command. I suspect it was lack of imagination that drove that choice more than anything else.
Jon A. Cruz - 16 Jan 2004 17:56 GMT > For that matter although I personally use a formatting style in > which all of the ifs in an if-then-else string are at the same indentation [quoted text clipped - 3 lines] > forces me to declare that some of the buttons on my panel are more > "important" than others. There's a simple fix for that.
In general, just put the 'else' and 'if' on the same line. All the formatters I use then in the context of 'else if' along with the original 'if' will have everything at the same level.
> Perhaps if there was some obvious reason why the argument of > setActionCommand is better as a String rather than on Object this wouldn't > bug me. But since the actual String value is never used, at least in > Localized code, I honestly don't see the point. The point is that with String.equals() you don't have to have identical objects. You can derive them at runtime, pull them from a file and substring, read them from network IO and substring, etc.
Even if they chose objects instead of Strings, you'd still have the same issue with '==' versus '.equals', where the definition of equivalence comes into play.
Gregory A. Swarthout - 16 Dec 2003 00:31 GMT > I am trying to handle ActionEvents spawned by various components in a dialog > panel in an efficient way. Perhaps I am missing something but I cannot find [quoted text clipped - 13 lines] > } // apply updates to the Register element > else ... Very nasty to use == to compare strings.
Greg
Thomas A. Russ - 16 Dec 2003 23:21 GMT > Very nasty to use == to compare strings. > > Greg Not necessarily. If the strings have been "intern"ed using the String.intern() method, then you can safely use == to compare them. As it happens, all literal strings have this property.
 Signature Thomas A. Russ, USC/Information Sciences Institute
Gregory A. Swarthout - 17 Dec 2003 18:38 GMT > > Very nasty to use == to compare strings. > > > > Greg > > Not necessarily. If the strings have been "intern"ed using the > String.intern() method, then you can safely use == to compare them. Not necessarily. Check out http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html for the explanation on why this can't always be counted on.
Greg
Thomas A. Russ - 17 Dec 2003 19:58 GMT > > > Very nasty to use == to compare strings. > > > [quoted text clipped - 7 lines] > for the > explanation on why this can't always be counted on. I guess I missed some subtle point there, but it seems to me the article referenced above just discusses whether interned strings with no references to them can be garbage collected.
It never indicated that the use of == on interned strings was rendered unsafe. In fact, I think the opposite point can be inferred -- namely that as long as you maintain a reference to the string, then the == test will succeed. If that were not true, then the entire motivation for having the intern method in the first place would disappear.
Following some of the promising links in the article just served to confirm the information about object identity for constant strings. My conclusion based on that is that it is, in fact, safe to use == on interned strings. For example, the conclusion to the reference to section 3.10.5 of the Java Language Specification notes:
* Literal strings within the same class (?8) in the same package (?7) represent references to the same String object (?4.3.1). * Literal strings within different classes in the same package represent references to the same String object. * Literal strings within different classes in different packages likewise represent references to the same String object. * Strings computed by constant expressions (?15.28) are computed at compile time and then treated as if they were literals. * Strings computed at run time are newly created and therefore distinct. * The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.
That seems to cover all of the cases that are of interest. The only way that garbage collection enters into this is if there are no references to the string, which would also seem to mean that it does not appear anywhere in the system as a constant. In fact, the example program used in the link took care to create the string from a character so as to avoid having the string constant present to inhibit GC.
 Signature Thomas A. Russ, USC/Information Sciences Institute
Filip Larsen - 17 Dec 2003 23:20 GMT > > If the strings have been "intern"ed using the > > String.intern() method, then you can safely use == to compare them. > > Not necessarily. Check out http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html
> for the explanation on why this can't always be counted on. Would you please explain where in the article it is claimed that intern'ed strings not always can be compared with the == operator?
Regards,
 Signature Filip Larsen
Roedy Green - 18 Dec 2003 00:02 GMT >Would you please explain where in the article it is claimed that >intern'ed strings not always can be compared with the == operator? Think he is referring to the limits on the total number of interned strings in various JVMS. It might be as low as 64K. That is still big enough for a many applications such as the Replicator which makes use of interned Strings to conserve space and speed up compares.
-- Canadian Mind Products, Roedy Green. Coaching, problem solving, economical contract programming. See http://mindprod.com/jgloss/jgloss.html for The Java Glossary.
Gregory A. Swarthout - 18 Dec 2003 18:00 GMT > > > If the strings have been "intern"ed using the > > > String.intern() method, then you can safely use == to compare them. [quoted text clipped - 6 lines] > Would you please explain where in the article it is claimed that > intern'ed strings not always can be compared with the == operator? "As you can see, s1 is interned and later garbage collected. s2 represents the same string value but is re-interned as a new object. In other words, Java does not prevent the "same" interned String from being different objects at different times."
Since == compares object references, not the contents of the string, it can return false under specific, deterministic situations. Why compare Strings by a method that *almost* always returns the same value as .equals() when .equals() itself returns the results you need 100% of the time?
Greg
Filip Larsen - 19 Dec 2003 18:18 GMT http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html
> > Would you please explain where in the article it is claimed that > > intern'ed strings not always can be compared with the == operator? [quoted text clipped - 6 lines] > Since == compares object references, not the contents of the string, > it can return false under specific, deterministic situations. No, the == operator cannot return false and the article does not claim this.
The article claims (as you quote) that the underlying object id may be different for two otherwise equal and intern'ed strings if those two strings exists on "opposite sides" of a period with no reference to the string in between. From this behaviour, however, it is not possible to end up with a scenario where the == operator will "fail" due to this (if you indeed can construct such code please post it here). Only code that assumes or depends on the value of System.identityHashCode() for intern'ed string to remaining fixed over time will be affected by this behaviour.
> Why compare Strings by a method that *almost* always returns the same > value as .equals() when .equals() itself returns the results you need > 100% of the time? I agree that equals in general should be preferred over the == operation. This is not because the == operator doesn't work, but because canonicalization (sp?) of an object should be a class implementation detail hidden as much as possible.
Regards,
 Signature Filip Larsen
Jon A. Cruz - 16 Jan 2004 17:43 GMT > else > if (actionCommand == CANCEL) [quoted text clipped - 12 lines] > an Object. If it were an Object then I could pass in an Integer containing > the desired case statement value. "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." - Donald Knuth
First question I have is how much more efficient is a switch statement in this specific case than a if-else structure? What's the actual performance difference you see in different runtime environments?
Second question would be how much performance is enough performance? In response to a user action, you usually don't care about things down into single-digit milliseconds or microseconds. Remember, you don't want to be optimizing the sytem idle loop.
Then there's the minor point of how you should maybe restructure your checks.
try changing to
else if ( CANCEL.equals(actionCommand) ) { ... } else if ( EDIT_FIELD.equals(actionCommand) ) ...
Remember, string literals are objects too.
Also, one of the first things VM's usually do for String.equals() is check the hashcode (which is a static int) for mismatches. So in the case of a string mismatch, all that is needed is a simple int compare. Combine this with some hotspot style VM and you might be suprised at the performance you actually get.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|