[ Team LiB ] |
6.3 CastsCasts 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 are 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 (ints, bytes, 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 having a small but measurable cost. With the JIT compiler running, the cast has no measurable effect (see Table 6-5): 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; } }
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, use type-specific collection classes instead of generic collection classes. Rather than use a standard List to store a list of Strings, you gain better performance with 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 variable 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 variable:[6]
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). |