Writing Efficient Loops
You should always factor loop invariant code out of a loop,
as in the following example:
for(int i = 0; i < vector.size(); i++) {
...
}
This code results in vector.size() getting called each time
through the loop, which is inefficient. If your container is likely
to have more than one element, it is much faster to assign the size
to a local variable. In addition, this example using pre-increment
(++i) results in smaller code than post-increment (i++). The optimized
code appears below:
int size = vector.size();
for(int i = 0; i < size; ++i) {
...
}
Alternatively, if the order in which you iterate over items
is not important, you can iterate backward. Iterating backward avoids
the extra local on the stack, and the comparison is also faster,
as the following example illustrates:
for(int i = vector.size() - 1; i = 0; --i) {
...
}
Optimizing Subexpressions
If you ever use the same expression twice, do not rely on
the compiler to optimize it for you. Use a local variable, as in
the following example:
one(i + 1); two(i + 1); // Avoid
int tmp = i + 1;
one(tmp);
two(tmp); // Prefer
Avoid java.util.Enumeration
Avoid using java.util.Enumeration unless you are using it
to hide data (in other words, returning an Enumeration of data rather
than the data itself). The following example shows a typical use
of an Enumeration:
for (Enumeration e = v.elements(); e.hasMoreElements();)
{
o = e.nextElement();
...
}
Asking a vector or hash table for an Enumeration object creates
unnecessary garbage and is slow. Instead, you can iterate over the
elements yourself, as in this example:
for(int i = v.size() - 1; i =0; --i) {
o = v.elementAt(i);
...
}
If the vector might be modified by another thread, you must
synchronize the iteration:
synchronized(v) {
for(int i = v.size() - 1; i =0; --i) {
o = v.elementAt(i);
...
}
}
In Java 2 Platform, Standard Edition (J2SE™), you would use
an Iterator for this, but Iterators are not available in Java 2
Platform, Micro Edition (J2ME™).
Returning null
If you are writing a public method that returns an object,
it should never return null unless the following occurs:
- a null is expected during normal program
operation.
- the javadoc @return parameter states that null is a possible
return value.
If a null is not normally expected, then the method should
throw an appropriate exception, which forces the caller to deal
with the problem explicitly. The caller is not expected to check
for a null return value, unless the documentation specifies otherwise.
Passing null into Methods
Do not pass null parameters into an application programming
interface (API) method unless the API Reference documentation explicitly
states that the method supports them.
Passing null into a Constructor
To avoid ambiguity when passing a null into a constructor,
you should use this form:
new someObject ((Object)null);
A class can have two or more constructors, such as SomeObject(String)
and SomeObject(Object), where passing in a null does not identify
which constructor to use. As a result, the compiler reports an error.
Not all supported constructors necessarily appear in the API Reference
documentation, because some constructors are for internal use only.
By casting the null to the appropriate object, you indicate precisely
which constructor the compiler should use. This practice also ensures
forward compatibility if later releases of the API add new constructors.
Optimizing Division Operations
Division operations are slow on the BlackBerry device because
its processor does not have a hardware divide instruction. When
you write code that divides a positive number by two, you should
use shift right by one instead. The following example
illustrates this:
midpoint = width / 2; //avoid this
int = width 1; //prefer this
This does not work for negative values. Only use shift
right () when you know you are dealing with a positive value.
It does not work for negative values.
Managing Garbage Collection
Avoid calling System.gc() to perform garbage collection. On
a full device, this could take two seconds. Let the virtual machine
(VM) collect garbage automatically.
Casting Using instanceof
It is more efficient to use instanceof instead of classCastException
to evaluate whether a cast succeeds. Here is an example of using
a try/catch block to catch the classCastException:
try {
(String)x.whatever();
} catch(ClassCastException e) {
// something else
}
Alternatively, you can use instanceof operator:
if(x instanceof String) {
(String)x.whatever();
} else {
// something else
}
Using instanceof is faster. The only time you should use the
try/catch block is when the failure of the cast is an exceptional
circumstance. The BlackBerry Java Development Environment (JDE)
compiler and the VM are optimized to perform only one class check in
the first block of code. This is true of any code in which the cast
is run immediately following a branch determined by an instanceof
check. Always perform the cast immediately after the branch so that
the optimization can be performed.
Using Longs for Unique Identifiers
You should use longs rather than strings for unique constants,
such as globally unique identifiers (GUIDs), hash table keys, and
state/context identifiers. So that unique identifiers remain unique
across all third-party application developers, you should use randomly-generated
keys based on a hash of some string. In the input string, you should include
enough information to make it unique.
Persistent storage on the BlackBerry device is limited. The
following programming tips should minimize the size of your compiled
code (.cod files) and maximize program speed. These tips are also
in the Developer Guide that is included with the BlackBerry JDE.
Set Appropriate Access
When you create code libraries, you can reduce the size of
your compiled code significantly if you use the appropriate access
modifiers for fields and methods. The following steps help reduce
the size of compiled code:
- Declare fields private whenever possible.
This is good coding practice and enables the compiler to optimize
the .cod file.
- When possible, use the default (package) access instead
of public access (that is, omit the public and protected keywords).
Use Shorthand for Evaluating Boolean Conditions
Use this efficient shorthand for evaluating Boolean conditions.
Instead of writing the code as in example 1, you could write the
code as in example 2:
// Example 1 - Avoid this
if(something_that_evaluates_to_true) {
return true;
} else {
return false;
}
// Example 2 - Do this
return(something_that_evaulates_to_true);
The resulting compiled code is shorter.
Avoid Creating Interfaces
When creating API libraries, avoid creating interfaces unless
you foresee multiple implementations of the API. Interfaces produce
longer, slower code.
Use Static Inner Classes
When you use an inner class to hide one class inside another,
but you do not need the inner class to reference the outer class
object, declare the inner class static. This suppresses the creation
of the reference to the outer class. For example, the following code
requires a reference to the outer class object:
class outer {
int i;
class inner {
inner() {}
int example() {return i;}
}
}
In contrast, this code only scopes the name of the inner class:
class outer {
static class inner {
}
}
is a shorter version of this code:
class outer {
}
class outer$inner {
}
The only time you should use a non-static inner class is when
you need access to data in the outer class from within methods of
the inner class. If you are using an inner class only for name scoping,
make the inner class static.
Make Classes Final
When creating code libraries, ensure that you mark a class
as final if you know it will never be extended. The presence of
the final keyword enables the compiler to generate faster code.
Use int Instead of Long
In Java, a long is a 64-bit integer. Since the device uses
a 32-bit processor, operations run two to four times faster if you
use an int instead of a long.
Using Static Strings
When defining static fields (also called class fields) of
type String, you can increase program speed by using static variables
(not final) instead of constants (final). The opposite is true for
primitive data types, such as int.
For example, you might create a String object:
private static final String x = "example";
For this static constant (denoted by the final keyword), a
temporary String instance is created every time you use the constant.
The compiler eliminates x and replaces it with the string example in
the bytecode, so that the virtual machine (VM) has to perform a
hash table lookup each time you reference x. In contrast, for a
static variable (no final keyword), the String is created once.
The VM performs the hash table lookup only when it initializes x,
which makes access faster.
Note: The BlackBerry Java Development Environment (JDE) compiler
(rapc) automatically marks any classes as final that are not extended
in an application .cod file.
Note: It is acceptable to have public constants (that is, final
fields), but you should always make variables private.