Java Language
Autoboxing
Поиск…
Вступление
Autoboxing - это автоматическое преобразование, которое компилятор Java делает между примитивными типами и соответствующими классами обертки объектов. Пример, преобразование int -> Integer, double -> Double ... Если преобразование идет другим путем, это называется распаковкой. Как правило, это используется в коллекциях, которые не могут содержать объекты, отличные от объектов, где необходимы примитивные типы бокса, прежде чем устанавливать их в коллекции.
замечания
Автобоксирование может иметь проблемы с производительностью при частом использовании в вашем коде.
Использование int и Integer взаимозаменяемо
Поскольку вы используете общие типы с классами утилит, вы часто можете обнаружить, что типы номеров не очень полезны при указании в качестве типов объектов, поскольку они не равны их примитивным аналогам.
List<Integer> ints = new ArrayList<Integer>();
List<Integer> ints = new ArrayList<>();
К счастью, выражения, которые оценивают int могут использоваться вместо Integer когда это необходимо.
for (int i = 0; i < 10; i++)
ints.add(i);
ints.add(i); утверждение эквивалентно:
ints.add(Integer.valueOf(i));
И сохраняет свойства из значения Integer#valueOf например, с теми же Integer объектами, которые кешируются JVM, когда он находится в диапазоне кеширования чисел.
Это также относится к:
-
byteиByte -
shortиShort -
floatиFloat -
doubleиDouble -
longиLong -
charиCharacter -
booleanиBoolean
Однако следует проявлять осторожность в неоднозначных ситуациях. Рассмотрим следующий код:
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
ints.add(3);
ints.remove(1); // ints is now [1, 3]
Интерфейс java.util.List содержит как метод remove(int index) (метод интерфейса List ), так и remove(Object o) (метод, унаследованный от java.util.Collection ). В этом случае бокс не происходит и не вызывается remove(int index) .
Еще один пример странного поведения кода Java, вызванного автобоксированием Целые числа со значениями в диапазоне от -128 до 127 :
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b); // true
System.out.println(c <= d); // true
System.out.println(c >= d); // true
System.out.println(c == d); // false
Это происходит потому, что >= оператор неявно вызывает intValue() который возвращает int while == сравнивает ссылки , а не значения int .
По умолчанию Java кэширует значения в диапазоне [-128, 127] , поэтому оператор == работает, потому что Integers в этом диапазоне ссылаются на одни и те же объекты, если их значения одинаковы. Максимальное значение кэшируемого диапазона можно определить с -XX:AutoBoxCacheMax опции -XX:AutoBoxCacheMax JVM. Итак, если вы запустите программу с помощью -XX:AutoBoxCacheMax=1000 , следующий код напечатает true :
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // true
Использование Boolean в выражении if
Из-за автоматического распаковки в операторе if можно использовать Boolean выражение:
Boolean a = Boolean.TRUE;
if (a) { // a gets converted to boolean
System.out.println("It works!");
}
Это работает while , do while и условие в операторах for .
Обратите внимание, что если Boolean равно null , в преобразовании будет NullPointerException .
Автоматическая распаковка может привести к NullPointerException
Этот код компилирует:
Integer arg = null;
int x = arg;
Но во время выполнения он будет сбой при исключении java.lang.NullPointerException во второй строке.
Проблема в том, что примитивный int не может иметь null значение.
Это минималистический пример, но на практике он часто проявляется в более сложных формах. NullPointerException не очень интуитивно понятное и часто мало помогает в поиске таких ошибок.
Положитесь на автобоксинг и автоматическую распаковку с осторожностью, убедитесь, что значения unboxed не будут иметь null значений во время выполнения.
Память и вычислительные накладные расходы на автобоксинг
Автобоксинг может иметь значительные накладные расходы памяти. Например:
Map<Integer, Integer> square = new HashMap<Integer, Integer>();
for(int i = 256; i < 1024; i++) {
square.put(i, i * i); // Autoboxing of large integers
}
как правило, потребляют значительный объем памяти (около 60 кб для 6 тыс. фактических данных).
Кроме того, целые числа в штучной упаковке обычно требуют дополнительных округлений в памяти и, таким образом, делают кэширование процессора менее эффективным. В приведенном выше примере доступ к памяти распространяется на пять разных местоположений, которые могут находиться в совершенно разных областях памяти: 1. объект HashMap , 2. объект Entry[] table , 3. объект Entry , 4. элемент ввода key слов (бокс примитивного ключа), 5. объект value entry (бокс примитивного значения).
class Example {
int primitive; // Stored directly in the class `Example`
Integer boxed; // Reference to another memory location
}
Чтение в boxed требует двух обращений к памяти, доступ к primitive только один.
При получении данных с этой карты, казалось бы, невинный код
int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
sumOfSquares += square.get(i);
}
эквивалентно:
int sumOfSquares = 0;
for(int i = 256; i < 1024; i++) {
sumOfSquares += square.get(Integer.valueOf(i)).intValue();
}
Как правило, приведенный выше код вызывает сбор и сбор мусора объекта Integer для каждой операции Map#get(Integer) . (Подробнее см. Примечание ниже).
Чтобы уменьшить эти накладные расходы, несколько библиотек предлагают оптимизированные коллекции для примитивных типов, которые не требуют бокса. В дополнение к тому, чтобы избежать накладных расходов на бокс, для этой коллекции потребуется примерно 4 раза меньше памяти на запись. В то время как Java Hotspot может оптимизировать автобоксинг, работая с объектами в стеке вместо кучи, невозможно оптимизировать накладные расходы памяти и вызванную память.
В потоках Java 8 также есть оптимизированные интерфейсы для примитивных типов данных, таких как IntStream которые не требуют бокса.
Примечание: типичная среда выполнения Java поддерживает простой кэш Integer и другой примитивный объект-оболочку, который используется фабричными методами valueOf и автобоксированием. Для Integer диапазон по умолчанию этого кеша составляет от -128 до +127. Некоторые JVM предоставляют параметр командной строки JVM для изменения размера и диапазона кеша.
Различные случаи Когда Integer и int могут использоваться взаимозаменяемо
Случай 1: используется вместо аргументов метода.
Если метод требует, чтобы объект класса-оболочки был аргументом. Затем, в качестве взаимозаменяемого аргумента может быть передана переменная соответствующего примитивного типа и наоборот.
Пример:
int i;
Integer j;
void ex_method(Integer i)//Is a valid statement
void ex_method1(int j)//Is a valid statement
Случай 2: При передаче возвращаемых значений:
Когда метод возвращает примитивную переменную типа, тогда объект соответствующего класса-оболочки может быть передан как возвращаемое значение взаимозаменяемо и наоборот.
Пример:
int i;
Integer j;
int ex_method()
{...
return j;}//Is a valid statement
Integer ex_method1()
{...
return i;//Is a valid statement
}
Случай 3: При выполнении операций.
Всякий раз, когда выполняются операции над числами, переменная примитива и объект соответствующего класса-оболочки могут использоваться взаимозаменяемо.
int i=5;
Integer j=new Integer(7);
int k=i+j;//Is a valid statement
Integer m=i+j;//Is also a valid statement
Pitfall : не забудьте инициализировать или присвоить значение объекту класса-оболочки.
При использовании объекта класса-оболочки и примитивной переменной взаимозаменяемо никогда не забывать или пропустить инициализацию или присвоение значения объекту класса-оболочки иначе он может привести к исключению нулевого указателя во время выполнения.
Пример:
public class Test{
Integer i;
int j;
public void met()
{j=i;//Null pointer exception
SOP(j);
SOP(i);}
public static void main(String[] args)
{Test t=new Test();
t.go();//Null pointer exception
}
В приведенном выше примере значение объекта не назначено и неинициализировано, и, таким образом, во время выполнения программа будет запущена в исключение нулевого указателя. Так же, как ясно из приведенного выше примера, значение объекта никогда не должно оставаться неинициализированным и неназначенным.