Автоупаковка довольно страшна. В то время как я полностью понимаю различие между ==
и .equals
Я не могу не помогать иметь следовать ошибку ад из меня:
final List<Integer> foo = Arrays.asList(1, 1000);
final List<Integer> bar = Arrays.asList(1, 1000);
System.out.println(foo.get(0) == bar.get(0));
System.out.println(foo.get(1) == bar.get(1));
Это печатает
true
false
Почему они делали это этот путь? Это что-то, чтобы сделать с кэшируемыми Целыми числами, но если это так почему они только не кэшируют все Целые числа, используемые программой? Или почему не делает JVM, всегда автоматической, распаковывают к примитиву?
Печать ложной лжи или верный верный была бы путем лучше.
Править
Я не соглашаюсь о поломке старого кода. При наличии foo.get(0) == bar.get(0)
возвратите true, Вы уже взломали код.
Не может это быть решенным на уровне компилятора путем замены Целого числа с интервалом в коде байта (как долго, поскольку этому никогда не присваивают пустой указатель),
Каждое целое число от -128 до 127 кэшируется java. Они сделали это якобы для повышения производительности. Даже если бы они захотели вернуться к этому решению сейчас, вряд ли они бы это сделали. Если бы кто-нибудь построил код в зависимости от этого, его код сломался бы, когда он был извлечен. Для хобби-кодирования это, возможно, не имеет значения, но для корпоративного кода люди расстраиваются и возникают судебные иски.
Все целые числа не могут быть кэшированы, потому что последствия для памяти будут огромными.
Потому что JVM не может знать, что вы хотите. Кроме того, это изменение может легко сломать устаревший код, не предназначенный для обработки этого случая.
Если JVM автоматически распаковывает примитивы при вызове ==, эта проблема станет БОЛЬШЕ запутанной. Теперь вам нужно помнить, что == всегда сравнивает ссылки на объекты, если только объекты не могут быть распакованы. Это вызовет еще более странные запутанные случаи, подобные тому, который вы указали выше.
Вместо того, чтобы слишком беспокоиться об этом, просто запомните следующее правило:
НИКОГДА не сравнивайте объекты с ==, если вы не собираетесь сравнивать их по их ссылкам. Если вы это сделаете, я не могу представить себе сценарий, в котором вы столкнулись бы с проблемой.
Целые числа
в байтовом диапазоне - это один и тот же объект, поскольку они кэшируются. Целые числа
вне диапазона байтов - нет. Если бы все целые числа были кэшированы, представьте себе требуемую память.
И из здесь
Результатом всей этой магии является то, что вы можете в значительной степени игнорировать различие между int и Integer, с некоторыми оговорками. Целочисленное выражение может иметь нулевое значение. Если ваша программа попытается автоответить null, она выдаст исключение NullPointerException. Оператор == выполняет сравнения эталонных идентификаторов в целочисленных выражениях и сравнения на равенство значений в выражениях типа int. Наконец, существуют затраты на производительность, связанные с упаковкой и распаковкой, даже если это делается автоматически
У многих людей возникают проблемы с этой проблемой, даже у людей, которые пишут книги о Java.
В Pro Java Programming всего в нескольких дюймах ниже автор говорит о проблемах с использованием автоматически упакованных целых чисел в качестве ключа в IdentityHashMap, он использует автоматические вставки целочисленных ключей в WeakHashMap. Используемые им примерные значения больше 128, поэтому его вызов сборки мусора завершается успешно. Если бы кто-то использовал его пример и использовал значения меньше 128, его пример потерпел бы неудачу (из-за постоянного кэширования ключа).
Можете ли вы представить, насколько плохой будет производительность, если каждый Integer
будет нести накладные расходы на интернализацию? Также не работает для new Integer
.
Язык Java (не проблема JVM) не всегда может автоматически распаковывать код, потому что код, разработанный для Java версии до 1.5, все равно должен работать.
Если вы полностью пропустите автобоксинг, вы все равно получите такое поведение.
final List<Integer> foo =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
final List<Integer> bar =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // true
System.out.println(foo.get(1) == bar.get(1)); // false
Будьте более явными, если вы хотите определенного поведения:
final List<Integer> foo =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
final List<Integer> bar =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // false
System.out.println(foo.get(1) == bar.get(1)); // false
Это причина, по которой Eclipse имеет автобоксинг как предупреждение по умолчанию.