Влияние на производительность автобокса

Обычно компилятор генерирует код для выполнения упаковки и распаковки. Но что делает компилятор, если коробочные значения не нужны? Компилятор (стандарт Oracle) достаточно умен, чтобы его оптимизировать?

Взгляните на этот метод:

public static void requireInRange(int index, Object[] array) {
    if(index < 0 || index >= array.length)
        throw new IndexOutOfBoundsException();
}

Единственная соответствующая информация array.length, поэтому было бы бесполезно, например, упаковывать каждое значение массива. Как в этом коде:

int[] anArray = {3, 4, 2};
requireInRange(3, anArray);

Будет ли компилятор вставлять код для упаковки каждого значения массива?

3 ответа

Решение

В вашем коде нет автобокса. На самом деле, учитывая:

public static void requireInRange(int index, Object[] array) {
   ...
}

int[] anArray = {3, 4, 2};
requireInRange(3, anArray); // DOES NOT COMPILE!!!

В то время как int может быть автоматически упакован в Integer, int[] НЕ получает автобокс в Integer[] по Java. Вы можете написать библиотечные функции для этого, но язык не облегчит это преобразование.

Это на самом деле источник многих путаницы, например, в отношении Arrays.asList(anIntArray) быть "сломанным", потому что вместо возвращения List<Integer> то, что возвращается, на самом деле является одним элементом List<int[]>,


Но как насчет производительности???

Цитата из Java Language Guide / Autoboxing:

Не рекомендуется использовать автобокс и распаковку для научных вычислений или другого числового кода, чувствительного к производительности. Integer не является заменой int; Автобокс и распаковка стирают различие между примитивными типами и ссылочными типами, но они не устраняют это.

Короче говоря, всякий раз, когда происходит автобокс, производительность, безусловно, занимает немного больше. Определенные вещи помогают облегчить это, например, механизм кэширования, встроенный в эти типы. Вот почему вы получаете следующее:

    System.out.println(
        ((Integer) 0) == ((Integer) 0)
    );
    // true

    System.out.println(
        ((Integer) 10000) == ((Integer) 10000)
    );
    // false (implementation-specific)

То, что произошло здесь, это то, что когда 0 автоматически упакован, не новый Integer Экземпляр фактически создан: значения в определенном диапазоне кэшируются для целей автобоксирования, чтобы повысить производительность. 10000 в большинстве реализаций, вероятно, выпадает из этого диапазона, но некоторые реализации JVM позволяют вам при необходимости указать диапазон кэша.


Но я просто хочу получить длину массива!!!

Есть много способов облегчить ваше requireInRange работать с любым типом массивов. К сожалению, работа с массивом примитивов Java часто означает много повторений. Это означает обеспечение перегрузок для int[], boolean[], byte[], Object[] и т. д. отдельно.

Более кратким вариантом является использование отражения, но у этого есть свои плюсы и минусы. Вообще говоря, рефлексия не должна быть предпочтительным решением для большинства сценариев.

Было сказано, что, java.lang.reflect.Array есть int getLength(Object array)static метод, который может вернуть длину ЛЮБОГО массива. Это небезопасно (как большинство механизмов отражения); Передача не-массива компилирует, но выдает IllegalArgumentException во время выполнения.

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

Будет ли компилятор вставлять код для упаковки каждого значения массива?

Компилятор отклонит код, потому что int[] не может быть передан в метод, который принимает Object[] параметр.

Автобокс происходит только для отдельных примитивных значений, а не для целых массивов.

Если вы сомневаетесь, вы можете предположить, что компилятор не оптимизирует код. Обычно это буквальный перевод кода.

Кроме того, если вы сомневаетесь, что JVM отлично справляется с оптимизацией кода во время выполнения. Я не предполагал бы, что это имеет какое-либо значение, если у вас нет веских причин (таких как профилировщик) подозревать, что это проблема.

Другие вопросы по тегам