Casts also have a cost. Casts that can be resolved at compile time can be eliminated by the compiler (and are eliminated by the JDK compiler). Consider the two lines:
Integer i = new Integer(3); Integer j = (Integer) i;
These two lines get compiled as if they were written as:
Integer i = new Integer(3); Integer j = i;
On the other hand, casts not resolvable at compile time must be
executed at runtime. But note that an instanceof
test cannot be fully resolved at compile time:
Integer integer = new Integer(3); if (integer instanceof Integer) Integer j = integer;
The test in the if
statement here cannot be
resolved by most compilers, because
instanceof
can return false
if the first operand
(integer
) is null
. (A more
intelligent compiler might resolve this particular case by
determining that integer
was definitely not
null
for this code fragment, but most compilers
are not that sophisticated.)
Primitive data type casts (int
s,
byte
s, etc.) are quicker than object data type
casts because there is no test involved, only a straightforward data
conversion. But a primitive data type cast is still a runtime
operation and has an associated cost.
Object type casts basically confirm that the object is of the required type. It appears that a VM with a JIT compiler is capable of reducing the cost of some casts to practically nothing. The following test, when run under JDK 1.2 without a JIT, shows object casts as having a small but measurable cost. With the JIT compiler running, the cast has no measurable effect (see Table 6-3):
package tuning.exception; public class CastTest { public static void main(String[] args) { Integer i = new Integer(3); int REPEAT = 500000000; Integer res; long time = System.currentTimeMillis( ); for (int j = REPEAT; j > 0 ; j--) res = test1(i); time = System.currentTimeMillis( ) - time; System.out.println("test1(i) took " + time); time = System.currentTimeMillis( ); for (int j = REPEAT; j > 0 ; j--) res = test2(i); time = System.currentTimeMillis( ) - time; System.out.println("test2(i) took " + time); ... and the same test for test2(i) and test1(i) } public static Integer test1(Object o) { Integer i = (Integer) o; return i; } public static Integer test2(Integer o) { Integer i = (Integer) o; return i; } }
Table 6-3. The Extra Cost of Casts
VM |
1.2 |
1.2 no JIT |
1.3 |
HotSpot 1.0 |
1.1.6 |
---|---|---|---|---|---|
Increase in time |
None |
>10% |
>20% |
~5% |
None |
However, the cost of an object type cast is not constant: it depends on the depth of the hierarchy and whether the casting type is an interface or a class. Interfaces are generally more expensive to use in casting, and the further back in the hierarchy (and ordering of interfaces in the class definition), the longer the cast takes to execute. Remember, though: never change the design of the application for minor performance gains.
It is best to avoid casts whenever possible, for example by creating
and using type-specific collection classes instead of using generic
collection classes. Rather than use a standard
List
to store a list of String
s, you gain better
performance by creating and using a
StringList
class. You
should always try to type the variable as precisely as possible. In
Chapter 9, you can see that by rewriting a sort
implementation to eliminate casts, the sorting time can be halved.
If a variable needs casting several times, cast once and save the object into a temporary variable of the cast type. Use that temporary instead of repeatedly casting; avoid the following kind of code:
if (obj instanceof Something) return ((Something)obj).x + ((Something)obj).y + ((Something)obj).z; ...
Instead, use a temporary:[45]
if (obj instanceof Something) { Something something = (Something) obj; return something.x + something.y + something.z; } ...
The revised code is also more readable. In tight loops, you may need to evaluate the cost of repeatedly assigning values to a temporary variable (see Chapter 7).
18.119.143.4