2013-04-23 3 views
19

В моей заявке obj.getClass().isArray() вызывается очень часто и становится узким местом приложения.
Я хочу эффективно проверить во время выполнения, если объект является массивом. Примитивный массив и массив объектов должны возвращать значение true.
Способ, которым я могу представить, - это instanceof все примитивные массивы, но не может обрабатывать такие типы, как int [] []. И приложение используется как lib, поэтому я не могу перечислить все типы.
Есть ли ключ к этому?Object.isArray() медленный, есть ли быстрый способ сделать это?

+2

Это не отвечает на ваш вопрос, но это приятное дополнение к обсуждению: http://stackoverflow.com/questions/219881/java-array-reflection-isarray-vs-instanceof –

+10

Я думаю, что вопрос в том, почему вы нужно часто называть этот метод? Надеюсь, что если бы был более быстрый способ, разработчики JDK основывали бы на себе свою реализацию. Если этого не происходит, вы должны дать более подробную информацию о реальной проблеме, которую пытаетесь решить. – Axel

+3

Ваши аргументы 'instanceof' не совсем корректны. 'int [] [] instanceof Object []' дает 'true' (так же, как' int [] instanceof Object'), поэтому эти случаи покрываются ... Не уверен, что это относится к вашему фактическому проблемному домену (см. Комментарий Акселя). –

ответ

8

Эталонный Я только что сделал, дал следующие результаты:

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

Benchmark был сделан с помощью Benchmark.java, называемых с Main.java.


После того, как обсуждались использование final переменных в приведенном выше тесте, увидеть новые результаты, используя локальную один:

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

Даже если длительности все немного больше (интересно кстати) , их порядок сохранился.

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) –

+0

@LukasEder [Benchmark.java] (http://pastebin.com/pskJ1tSy) и [Main.java] (http: //pastebin.com/KbptdWaB). Я должен сказать, что это инструмент, который я разработал сам по себе, поэтому я надеюсь, что в этом нет никакой ошибки. Не стесняйтесь сказать мне, если вы заметили одного, возможно, я сделал что-то не так :) – sp00m

+0

Да, я подозреваю, что 'boolean b = s instanceof Object [];' оптимизирован компилятором, так как '' '' 'final ', и вышеприведенное выражение, таким образом, всегда оценивается как true ... –

7

isArray() - самый эффективный способ проверить, является ли объект экземпляром массива во время выполнения. Если производительность является проблемой, вы можете использовать один из следующих методов для решения этой проблемы:

  • рефакторинга кода, так объекты массивов и объекты без массива обрабатываются по отдельности, так что результаты isArray() известны во время компиляции ,
  • Используйте локальные переменные и/или аргументы для кэширования значения isArray() во время операции, поэтому его нужно только вызывать один раз.
+2

Я согласен с рефакторингом, но может ли кто-нибудь ответить на их утверждения, что 'isArray()' намного медленнее, чем 'instanceof'? Я чувствую, что кеширование результата может принести больше вреда, чем решить проблемы здесь! –

+0

Кажется, что ваше (необоснованное) утверждение о том, что 'usArray()' является самым быстрым, неверно - см. Ответ sp00n. Однако я согласен с предложениями. – Bohemian

+0

@Bohemian: тест sp00m может быть неточным, см. Комментарии ... –

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. (Я не удивлюсь, что вы получили бы противоположные «результаты», выполнив тесты в другом порядке ...) –

+0

@StephenC: Конечно, я подумал об этом и попытался в обоих направлениях. Я обновлю результаты ... –

Смежные вопросы