Object.isArray() медленный, есть ли быстрый способ сделать это?

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

Ответ 1

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

{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

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

Затем Benchmark.java вызывается с помощью этого нового Main.java.


И используя примитивный массив, вызываемый с помощью этого другого Main.java:

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

Все тот же порядок результатов.

Ответ 2

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

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

Ответ 3

Из ваших комментариев, я заключаю, что вы можете страдать от интерпретирующей ошибки при исследовании результатов профилирования. Инструментальное устройство уровня профилировщика может сильно искажать вызовы 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. Конечно, есть много разных способов переписать мой тест, но вы получите эту идею.