|
January 20, 1998 This issue presents tips, techniques, and sample code for the following topics:
String versus StringBuffer. What's the fastest way to do this? One approach is to set up an empty string and append characters to it in turn, using the += operator for appending, as shown in the following example:
This approach works, but overlooks the fact that in the Java language, strings are immutable (that is, they never change). Therefore, the operation shown above consists of copying the current value of "result" to a temporary buffer, appending the character, and creating a new String object that result then references. Another way of saying this is that in:
the printed result will be "abc," because reassigning to string1 doesn't change the reference from string2 to the string "abc." A faster approach: A much faster approach to detabbing uses StringBuffer, a class that supports mutable strings. Operations such as appending are directly supported with StringBuffer, and the result can be converted to a String at any time. To show how this approach works in practice, here is an example of detabbing text using String (detab1) and StringBuffer (detab2):
This example first runs some test data through both methods to ensure they are equivalent, and then does a timing to compare the two. The second method, using StringBuffer, runs approximately six times as fast as the first method using String. When you're tuning programs that do a lot of character processing, it's always a good idea to keep an eye on how much work the program is doing for each character, and the example given above illustrates how character data in the Java language can be efficiently manipulated.
Using javap
the result is:
Other options include
to print Java1 Virtual Machine bytecodes,
to display line number and local variable tables (you need to compile perf.java with -g for this to work), and
to print private methods and fields in addition to the public ones. javap is a handy tool for digging beneath the surface to find out what's really going on in a .class file.
Followup on Tech Tips: December 16, 1997 The application is one where Java source code is being parsed. The parse tree contains non-terminal and terminal nodes, and there are several hundred different node types, representing grammar productions in Java (such as "Primary Expression"). These different types must be represented in some way. Two approaches are to use a "type" field, as was done in the example, or to define a subclass for each type. The latter approach implies several hundred classes, which is not manageable. The other issue is whether to factor out into distinct classes the three different cases, one for a terminal node, one where a node has a single child node, and one where a node has multiple children. This can be handled as shown in the example, but can also be represented via an abstract superclass, with three subclasses:
This approach will work, and is in many ways more elegant than the approach presented in Tech Tips: December 16, 1997. But there are a couple of tradeoffs to be made. One is that you have four classes instead of one, which makes the application somewhat harder to manage. The other issue is one of performance; the Node superclass cannot be declared "final." A method such as getType or getChild, invoked through a Node superclass reference, will be slower than an equivalent method invoked on a single final class of type Node.
_______ | ||||||||||
|
| ||||||||||||