In my application obj.getClass().isArray()
is called very frequently and become the bottleneck of the app.
I want to check efficiently at run-time if an object is an array. Primitive array and object array should return true.
The way I can imagine is to instanceof
all primtive arrays, but cannot handle types like int[][]. And the app is used as lib, so I cannot list all types.
Is there any clue for that?
问题:
回答1:
A benchmark I've just done gave the following results:
{s instanceof Object[]} spends 44ms {s.getClass().getName().charAt(0) == '['} spends 58ms {s.getClass().isArray()} spends 303ms
Benchmark has been done using Benchmark.java, called with Main.java.
After having discussed the use of a final
variable in the above benchmark, see the new results using a local one:
{s instanceof Object[]} spends 83ms {s.getClass().getName().charAt(0) == '['} spends 93ms {s.getClass().isArray()} spends 354ms
Even if the durations are all a bit longer (interesting btw), their order has been preserved.
Benchmark.java has been then called with this new Main.java.
And using a primitive array called with this other Main.java:
{a instanceof int[]} spends 71ms {a.getClass().getName().charAt(0) == '['} spends 82ms {a.getClass().isArray()} spends 340ms
Still the same results order.
回答2:
isArray()
is the most efficient way to check if an object is an instance of an array at runtime. If the performance is a problem, you can use one of the following methods to address it:
- Refactor your code so array objects and non-array objects are handled separately, so the results of
isArray()
are known at compile time. - Use local variables and/or arguments to cache the value of
isArray()
during an operation, so it only needs to be called once.
回答3:
From your comments, I conclude that you may be suffering from an interpretive mistake when investigating profiling results. Your profiler's method-level instrumentation might be heavily crippling getClass()
and isArray()
calls, while being unimpressed by instanceof
expressions. In other words, you're probably measuring the measuring overhead of your profiler, here.
Besides, in a quick benchmark, I cannot back your claim. I've run the following, very silly test:
public class Test { public static void main(String[] args) { final int rep = 10000000; Object[] o = { null, 1, "x", new Object[0], new Object[0][], new int[0], new int[0][] }; // "Warmup" to avoid potential JVM startup overhead long x = 0; for (int i = 0; i < rep; i++) { x+=checkInstanceOf(o); } for (int i = 0; i < rep; i++) { x+=checkIsArray(o); } for (int i = 0; i < rep; i++) { x+=checkClassName(o); } // Actual test long t1 = System.nanoTime(); for (int i = 0; i < rep; i++) { x+=checkInstanceOf(o); } long t2 = System.nanoTime(); for (int i = 0; i < rep; i++) { x+=checkIsArray(o); } long t3 = System.nanoTime(); for (int i = 0; i < rep; i++) { x+=checkClassName(o); } long t4 = System.nanoTime(); System.out.println(t2 - t1); System.out.println(t3 - t2); System.out.println(t4 - t3); } private static int checkInstanceOf(Object[] o) { int i = 0; for (Object x : o) { if (x instanceof Object[]) i++; // Perform some logic else if (x instanceof boolean[]) i++; // to keep the compiler or else if (x instanceof byte[]) i++; // the JVM from optimising else if (x instanceof short[]) i++; // this code away else if (x instanceof int[]) i++; else if (x instanceof long[]) i++; else if (x instanceof float[]) i++; else if (x instanceof double[]) i++; else if (x instanceof char[]) i++; } return i; } private static int checkIsArray(Object[] o) { int i = 0; for (Object x : o) { if (x != null && x.getClass().isArray()) i++; } return i; } private static int checkClassName(Object[] o) { int i = 0; for (Object x : o) { if (x != null && x.getClass().getName().charAt(0) == '[') i++; } return i; } }
I'm getting:
394433000 // instanceof 110655000 // getClass().isArray() 396039000 // getClass().getName().charAt(0) == '['
So you cannot generally claim getClass().isArray()
to be slower than a thorough set of instanceof
checks. Of course, there is a lot of different ways to rewrite my test, but you get the idea.