Programming Tips: General Coding Tips

by Retired ‎02-16-2010 10:36 AM - edited ‎02-16-2010 10:37 AM (2,438 Views)

Summary


This article applies to the BlackBerry® wireless devices based on Java™.




Description


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.