Long, Integer, Double, Float, Short, Boolean, Character, Byte - это версии-оболочки примитивов - long, int, double, float, short, boolean, char, byte. Оболочки позволяют хранить значения в коллекциях, таких как списки, наборы, карты. Дополнительным преимуществом классов-оболочек перед примитивными является возможность выполнения метода над этим объектом.

Например, мы можем:

counter.toString();

or

counterB.compareTo(counterA);

например, на Длинный, целочисленный, двойной или плавающий объект.

Тип оболочки хранится в памяти в куче, а стек содержит ссылку на объект в куче. Примитивные значения располагаются только в стеке.

Арифметические операции над оберткой и примитивом

Давайте проанализируем это

public static void main(String[] args) {
    int repeats = 40000000;
    long time;

    time = System.currentTimeMillis();
    long counterA = 0L;
    for (int i = 0; i < repeats; i++) {
        counterA = counterA + 4L;
    }
    System.out.println(counterA + " A: " + (System.currentTimeMillis() - time) + " ms");

    time = System.currentTimeMillis();
    Long counterB = 0L;
    for (int i = 0; i < repeats; i++) {
        counterB = counterB + 4L;
    }
    System.out.println(counterB + " B: " + (System.currentTimeMillis() - time) + " ms");
}

То же самое можно сказать и о примитивных длинных файлах и обертках типа Long.

Та же задача, но разница во времени обработки огромна. Лонги суммируются гораздо дольше:

Причина тому - ненужное размещение объектов. Каждое добавление создает новый объект Long. Есть много объектов, которые позже нужно будет убрать сборщиком мусора.

Типы оболочки для расчета не нужны.

Вам нужна оболочка или примитив - используйте правильный метод преобразования parseType / valueOf

Существует значительная разница между методами, которые анализируют строку до примитивного числа, например:

  • Long.parseLong
  • Integer.parseInt
  • Double.parseDouble
  • Float.parseFloat

и методы, возвращающие тип оболочки для этого числа:

  • Long.valueOf
  • Integer.valueOf
  • Double.valueOf
  • Float.valueOf

Очень часто valueOf используется для получения неправильного примитива.

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

Я провел тест, который сравнивает время обработки Long.parseLong и Long.valueOf для разных чисел много раз - 20000 в тесте.

Это код:

public static void main(String[] args) {
    int repeats = Integer.parseInt(args[0]);

    List<String> numbers = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        numbers.add(String.valueOf(i));
    }

    long time;

    time = System.currentTimeMillis();
    long sumOfLongsA = 0L;
    for (int i = 0; i < repeats; i++) {
        for (String number : numbers) {
            sumOfLongsA += Long.parseLong(number);
        }
    }
    System.out.println(sumOfLongsA + " Parse: " + (System.currentTimeMillis() - time) + " ms");

    time = System.currentTimeMillis();
    long sumOfLongsB = 0L;
    for (int i = 0; i < repeats; i++) {
        for (String number : numbers) {
            sumOfLongsB += Long.valueOf(number);
        }
    }
    System.out.println(sumOfLongsB + " value: " + (System.currentTimeMillis() - time) + " ms");
}

Результат выглядит следующим образом:

Как вы видите, использование обертки типа long делает всю обработку на 80% медленнее.

Тот же тест был повторен для целых чисел, чисел с двойной точностью и чисел с плавающей запятой.

Как видите, когда дело доходит до десятичных чисел, разница между суммированием типа оболочки и примитивных чисел меньше, но все же типы оболочки все равно на 30% медленнее.

Распаковка и автобокс

Распаковка и автоматическая упаковка делают описанную выше проблему еще более скрытой.

public static void main(String[] args) {
    int repeats = Integer.parseInt(args[0]);

    List<String> numbers = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        numbers.add(String.valueOf(i));
    }

    long time;

    time = System.currentTimeMillis();
    float sumOfLongsB = 0;
    for (int i = 0; i < repeats; i++) {
        for (String number : numbers) {
            sumOfLongsB += calculateValueObject(number);
        }
    }
    System.out.println(sumOfLongsB + " value: " + (System.currentTimeMillis() - time) + " ms");

    time = System.currentTimeMillis();
    double sumOfLongsA = 0;
    for (int i = 0; i < repeats; i++) {
        for (String number : numbers) {
            sumOfLongsA += calculateValuePrimitive(number);
        }
    }
    System.out.println(sumOfLongsA + " Parse: " + (System.currentTimeMillis() - time) + " ms");


}

private static float calculateValuePrimitive(String number) {
    final int baseCalculation = Integer.parseInt(number);
    return baseCalculation * 1.23f;
}

private static float calculateValueObject(String number) {
    final int baseCalculation = Integer.valueOf(number);
    return baseCalculation * 1.23f;
}

Разберем тот же случай, но с расчетной методикой. Каждый метод расчета делает то же самое. Разница в том, что в calculateValueObject Integer. valueOf использовался вместо Integer. parseInt.

Как вы видите, дальнейшие вычисления выполняются с примитивом int, поэтому целое число-оболочка доступно только на мгновение в:

final int baseCalculation = Integer.valueOf(number);

где он распакован до примитивного типа int.

Эта ошибка может дорого обойтись:

Та же проблема может возникнуть, когда вы передаете примитив методу, которому требуется тип оболочки. Например.:

private static float calculateValuePrimitive(String number) {
    final int baseCalculation = Integer.parseInt(number);

    return calculateWithVatTax(baseCalculation);
}

private static float calculateWithVatTax(Integer baseCalculation) {
    return baseCalculation * 1.23f;
}

private static float calculateValueObject(String number) {
    final int baseCalculation = Integer.valueOf(number);
    return baseCalculation * 1.23f;
}

Автобокс может существенно повлиять на производительность:

В этом случае автобокс стал на 35% медленнее.

Резюме

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