Developers
Local Navigation
Topics within this section include:
- Checking for a 0-length string
- Loops
- Class & instance variable defaults
- object instanceof <type> is false if object is null
- String literals
- Division by 2
- instanceof keyword
- Making smaller .cod files
- Avoid Object.getClass()
- Avoid Vector.ensureCapacity()
- Loop induction variables
- Common sub-expressions
- Use int not long
- Avoid using Strings as keys or GUIDs
Checking for a 0-length string
Inefficient:
if( str.equals( "" ) ) {
Efficient:
if( str.length() == 0 ) {
Loops
This loop:
int length = v.size();
for( int i = 0; i < length; ++i ) {
... executes more quickly than this loop:
for( int i = 0; i < v.size(); ++i ) {
This loop executes more quickly than either of the above, and should be used when direction is not important:
for( int i = v.size()-1; i >= 0; --i ) {
CAUTION: Include a comment if the size of v can change:
// vector's size can change during loop
for ( int i = 0; i < v.size(); ++i ) {
Class & instance variable defaults
In Java, instance variables within a class are always set to a default value. This does not apply to variables declared within a method, which must be explicitly initialized before use.
From the Java Language Specification:
Each class variable, instance variable, or array component is initialized with a default value when it is created (§15.9, §15.10):
| Type | Default Value |
|---|---|
| byte | (byte)0 |
| short | (short)0 |
| int | 0 |
| long | 0L |
| float | Positive 0.0f |
| double | Positive 0.0d |
| char | null ‘’ |
| boolean | false |
| class, interface and array types | Null |
So, writing:
private boolean _foo = false;
... is redundant and has a negative impact on code size and performance.
Instead, use:
private boolean _foo;
If you like the commenting effect, use this form instead:
private boolean _foo; // = false
object instanceof <type> is false if object is null
Both of these:
if ( obj != null && obj instanceof Foo ) {
if ( obj == null || !( obj instanceof Foo )) {
... can be reduced to:
if ( obj instanceof Foo ) {}
if ( ! ( obj instanceof Foo ) ) {}
... producing smaller and faster code.
String literals
Avoid:
new String( "literal" );
The expression ″literal″ is an instance of java.lang.String so creating another object via:
new String( "literal" );
... is wasting time and space. The only use of the String(String) constructor should be in the Sun Technology Compatibility Kit (TCK), since the new copy of the string is indistinguishable from the original except that it has a different reference. Recall that java.lang.String cannot be modified after they are created, so creating carbon copies often isn't necessary.
Another case takes this form:
new String( "found " + n + " item(s)" );
Once again, a useless extra String is created. Use this form instead:
String foo = "found " + n + " item(s)";
Division by 2
Code that divides a positive number by 2:
midpoint = width / 2;
... should use shift-right by one instead:
midpoint = width >> 1;
Note: This only works with positive integers. Negative numbers require divide.
instanceof keyword
The compiler has a special case construct to improve the performance of this common idiom:
if( x instanceof A ) {
A y = (A)x;
...
}
In simple terms, if 'x' is a of type 'A', then interpret 'x' as being of type 'A' and store it in 'y'. Where 'y' is declared to be of type 'A'.
Close inspection of what javac generates for this construct reveals byte code like this:
load x
instanceof A // is 'x' of type 'A'?
if false goto label
load x
checkcast A // interpret 'x' as type 'A'
store y
...
label:
It turns out that instanceof and checkcast have to do almost exactly the same work. The main difference is that instanceof accepts a reference on the stack and produces a Boolean on the stack. checkcast accepts a reference on the stack and either leaves it there, now of the checked type, or throws a ClassCastException. The bottom line is that all the type checking work is done twice.
Another difference between instanceof and checkcast is that null is never an instanceof anything, whereas null will checkcast to any reference type. This means that it is unnecessary to do a null check along with the instanceof in the expression of an if statement.
The compiler recognizes similar byte code sequences and transforms them into an equivalent alternate sequence that only does the type checking once. This is a good thing.
Unfortunately, the compiler needs the javac generated byte code to be close to what it expects and not to contain other constructs that would invalidate the correctness of the transformation. In order to ensure that Java source will be translated into byte code that is recognizable by the compiler, it is necessary to follow a few simple rules.
The expressions used to load 'x' first for the instanceof and then again for the checkcast must be identical. They may only consist of data read operations. They may not write to a variable, or invoke a method.
The instanceof and checkcast must be separated by only the conditional branch and the second load of 'x'. The if expression shouldn't check anything else after the instanceof and the first statement after the inside the if block should be the cast.
These won't work:
if( fetch() instanceof A ) {
A y = (A)fetch();
...
}
if( x instanceof A && otherResult ) {
A y = (A)x;
...
}
if( !(x instanceof A)) {
...
} else {
A y = (A)x;
...
}
Passing the result of the cast to a method also won't work because of the code generated to set up the method invocation. It is usually acceptable to use the result of the cast as the reference for a method invocation.
if( x instanceof A ) {
foo( (A)x ); // no good
...
}
if( x instanceof A ) {
((A)x).bar(); // ok
...
}
The best source construct is this:
if( x instanceof A ) {
A y = (A)x;
...
}
Then y can be used wherever it is needed.
To wrap together all the above cases into a single example that works:
x = fetch();
if( otherResult && x instanceof A ) {
A y = (A)x;
foo( y );
y.bar();
...
} else {
...
}
Making smaller .cod files
- Declare members as private whenever possible. This allows the compiler to omit the name from the .cod file.
- Don't declare members protected unless you really think somebody is going to want to override them. Protected member names end up in the .cod file.
- Prefer package access over public access (package access is used when you omit the private, public and protected keywords).
- A class that will never be extended should be marked as final. The presence of the final keyword allows the compiler to generate code that invokes methods directly, without using the virtual method table.
This has two benefits:
- Direct invocation is faster. There is no indirect lookup required to find the new code offset.
- Direct invocation, within a .cod file, is smaller. It requires no fix up information to be generated for the invocation. This makes for smaller .cod files. The fix up information needs 10 bytes for the first invocation and 2 more bytes for each additional invocation.
Avoid Object.getClass()
Avoid this method. It is only included for TCK compatibility and is not efficient.
Avoid Vector.ensureCapacity()
Avoid this method. It is only included for TCK compatibility. If called, that vector will stop using the Array.resize() function.
Loop induction variables
Do not rely on javac to do loop optimizations. Always attempt to do your own induction variable optimizations.
Avoid this:
// calculates 2*I+1 every loop iteration
for( int i = 0; i < n; ++i )
x[i] = x[2*i+1];
Instead, use:
// introduce a new variable j==2*I+1
for( int i=0, j=1; i < n; ++i, j += 2 )
x[i] = x[j]
Common sub-expressions
If you ever use the same expression twice, don't rely on javac to optimize it for you. Use a local.
For example, avoid this:
foo( i+1 ); bar( i+1 );
Instead use:
int tmp = i+1; foo(tmp); bar(tmp);
Use int not long
A Java long is a 64-bit integer (unlike a C++ long which is 32-bits). Since Java-based BlackBerry handhelds use 32-bit processors, things will go 2 to 4x faster if you use an int instead of a long wherever possible.
Avoid using Strings as keys or GUIDs
While classes like ResourceBundle and Hashtable may encourage using String keys in Big Java, this practice is entirely inappropriate in a wireless environment.
Unlike C++, where a literal string is just another constant, a String in Java is a first class object, which must be instantiated. Each time a ″literal″ or a static final String constant is referenced, it creates a garbage object. Non-final static Strings must be instantiated on a per-process basis.
You would not use malloc() to create constant values in C++, and you should not do it in Java. Integer constants should always be used in place of Strings when possible. With a little bit of discipline, integer keys can be just as flexible and upwardly compatible as String keys.