2013-04-23 101 views
19

在我的应用程序obj.getClass().isArray()被称为非常频繁,成为应用程序的瓶颈。
我想在运行时高效地检查对象是否是数组。 基元数组和对象数组应该返回true。
我可以想象的方式是instanceof所有原始数组,但不能处理int [] []类型。该应用程序用作lib,所以我不能列出所有类型。
有什么线索吗?Object.isArray()很慢,有没有一种快速的方法来做到这一点?

+2

这不回答你的问题,但它是一个很好的补充讨论:http://stackoverflow.com/questions/219881/java-array-reflection-isarray-vs-instanceof – 2013-04-23 13:27:35

+10

我认为这个问题是为什么你必须经常调用该方法?我希望如果有更快的方法,JDK开发人员会将其实现基于它。如果没有,你必须提供你正在试图解决的实际问题的更多细节。 – Axel 2013-04-23 13:30:45

+3

你的'instanceof'推理不完全正确。 'int [] [] instanceof Object []'产生'true'(就像'int [] instanceof Object'一样),所以这些情况都被覆盖了......不确定这是否与你的实际问题域相关, Axel的评论)。 – 2013-04-23 13:33:35

回答

8

我刚刚做了一个基准测试结果如下:

{s instanceof Object[]} spends 44ms 
{s.getClass().getName().charAt(0) == '['} spends 58ms 
{s.getClass().isArray()} spends 303ms 

基准一直使用Benchmark.java完成,调用Main.java


已经讨论了上述基准使用final变量后,使用当地的一个见到新成效:

{s instanceof Object[]} spends 83ms 
{s.getClass().getName().charAt(0) == '['} spends 93ms 
{s.getClass().isArray()} spends 354ms 

即使持续时间都长一点(有趣的BTW) ,他们的顺序一直保存下来。

Benchmark.java已被随后调用这个新的Main.java


而使用称为与该其他Main.java基本数组:

{a instanceof int[]} spends 71ms 
{a.getClass().getName().charAt(0) == '['} spends 82ms 
{a.getClass().isArray()} spends 340ms 

仍然相同的结果顺序。

+0

您可以发布基准代码吗?我得到了[相反的结果](http://stackoverflow.com/a/16171137/521799) – 2013-04-23 13:58:47

+0

@LukasEder [Benchmark.java](http://pastebin.com/pskJ1tSy)和[Main.java](http: //pastebin.com/KbptdWaB)。我必须说这是我自己开发的工具,所以我希望它没有错。不要犹豫,告诉我,如果你注意到一个虽然,也许我做错了:) – sp00m 2013-04-23 14:05:45

+0

是的,我怀疑'布尔b = s instanceof对象[];'是由编译器优化,因为's'是'最后'和上面的表达式因此总是评估为真... – 2013-04-23 14:08:04

7

isArray()是在运行时检查对象是否为数组实例的最有效方法。如果性能是一个问题,你可以使用下列方法之一来解决这个问题:

  • 重构代码,以便数组对象和非数组对象是分开处理的,所以isArray()结果在编译时已知。
  • 在操作过程中使用局部变量和/或参数来缓存isArray()的值,因此只需调用一次即可。
+2

我同意重构,但任何人都可以请他们声明'isArray()'比'instanceof'慢得多吗?我认为缓存结果可能会造成更多的伤害,而不是解决问题! – 2013-04-23 14:03:03

+0

看来你的(未经证实的)声明'usArray()'最快是错误的 - 请参阅sp00n的答案。尽管我同意这些建议。 – Bohemian 2013-04-23 14:24:28

+0

@波希米亚:sp00m的测试可能不准确,请参阅评论... – 2013-04-23 14:26:37

2

从您的意见中,我可以得出结论,您在调查分析结果时可能会遭受解释错误。您的配置文件的方法级别的检测可能会严重影响getClass()isArray()调用,而对instanceof表达式的影响不大。换句话说,你可能在这里测量你的轮廓仪的测量开销。

另外,在一个快速的基准测试中,我不能支持你的说法。我已经运行下面的,很无聊的测试:

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; 
    } 
} 

我越来越:

394433000 // instanceof 
110655000 // getClass().isArray() 
396039000 // getClass().getName().charAt(0) == '[' 

所以你不能一般要求getClass().isArray()比彻底的一套instanceof检查慢。当然,有很多不同的方法来重写我的测试,但你明白了。

+1

这个基准是有缺陷的。它不考虑JVM热身。 (我不会感到惊讶,你会通过以另一种顺序进行测试而得到相反的“结果”......) – 2013-04-23 14:08:36

+0

@StephenC:我当然想到了这一点,并试过两种方法。我会更新结果... – 2013-04-23 14:13:07

相关问题