Начиная с Java 5, у нас был бокс / распаковка примитивных типов, чтобы они были int
упакованы java.lang.Integer
, и так далее, и так далее.
В последнее время я вижу много новых Java-проектов (для которых определенно требуется JRE как минимум версии 5, если не 6), которые используют, int
а не java.lang.Integer
, хотя гораздо удобнее использовать последний, так как в нем есть несколько вспомогательных методов для преобразования до long
значений и др.
Почему некоторые все еще используют примитивные типы в Java? Есть ли ощутимая выгода?
java
primitive
primitive-types
autoboxing
jdk1.5
Нафтули Кей
источник
источник
new IntegeR(5) == new Integer(5)
по правилам, оценивать как ложное.Ответы:
В « Эффективной Java» Джошуа Блоха , пункт 5: «Избегайте создания ненужных объектов», он публикует следующий пример кода:
и это займет 43 секунды, чтобы бежать. Взятие Long в примитив приводит к снижению до 6,8 секунд ... Если это хоть какой-то признак того, почему мы используем примитивы.
Отсутствие равноправия с родной ценностью также вызывает озабоченность (
.equals()
по сравнению с довольно многословным==
)для бизиклопа:
Результаты в:
РЕДАКТИРОВАТЬ Почему (3) возвращается
true
и (4) возвращаетсяfalse
?Потому что это два разных объекта. 256 целых чисел, ближайших к нулю [-128; 127] кэшируются JVM, поэтому они возвращают один и тот же объект для них. Однако за пределами этого диапазона они не кэшируются, поэтому создается новый объект. Чтобы сделать вещи более сложными, JLS требует, чтобы было кэшировано не менее 256 маховиков. Реализаторы JVM могут добавить больше, если захотят, это означает, что это может выполняться в системе, в которой кэшируются ближайшие 1024, и все они возвращают true ... #awkward
источник
i
были объявленыLong
!==
оператор выполняет сравнения ссылочных идентификаторов вInteger
выражениях и сравнения равенства значений вint
выражениях.Integer.equals()
существует по этой самой причине. Вы никогда не должны использовать==
для сравнения значений в любом не примитивном типе. Это Java 101.Autounboxing может привести к трудно обнаружить NPE
В большинстве ситуаций нулевое назначение
in
намного менее очевидно, чем выше.источник
Типы в штучной упаковке имеют худшую производительность и требуют больше памяти.
источник
Примитивные типы:
Теперь оцените:
Это
true
. Вряд ли удивительно. Теперь попробуйте в штучной упаковке:Теперь оцените:
Это
false
. Наверное. Зависит от времени выполнения. Достаточно ли этой причины?источник
Помимо проблем с производительностью и памятью, я хотел бы затронуть еще одну проблему: без
List
нее будет нарушен интерфейсint
.Проблема в перегруженном
remove()
методе (remove(int)
противremove(Object)
).remove(Integer)
всегда будет разрешать вызов последнего, поэтому вы не можете удалить элемент по индексу.С другой стороны, при добавлении и удалении
int
:источник
Vector
былоremoveElementAt(int)
с самого начала.remove(int)
был представлен со структурой коллекций в Java 1.2.List
разрабатывался API, не существовало ни Generics, ни Autoboxing, поэтому не было шансов смешатьсяremove(int)
иremove(Object)
…Можете ли вы представить себе
цикл с java.lang.Integer вместо этого? Java.lang.Integer является неизменным, поэтому каждое приращение в цикле будет создавать новый объект java в куче, а не просто увеличивать int в стеке с помощью одной инструкции JVM. Представление будет дьявольским.
Я бы очень не согласился, что гораздо удобнее использовать java.lang.Integer, чем int. Напротив. Автобокс означает, что вы можете использовать int там, где в противном случае вы были бы вынуждены использовать Integer, а java-компилятор позаботится о вставке кода для создания нового объекта Integer для вас. Автобокс - это все, что позволяет вам использовать int там, где ожидается Integer, с компилятором, вставляющим соответствующую конструкцию объекта. Это никоим образом не устраняет и не уменьшает потребность в int в первую очередь. С автобоксом вы получаете лучшее из обоих миров. Вы получаете Integer, созданный для вас автоматически, когда вам нужен Java-объект на основе кучи, и вы получаете скорость и эффективность int, когда вы просто выполняете арифметические и локальные вычисления.
источник
Примитивные типы намного быстрее:
Целое число (все числа, а также строка) является неизменным типом: после создания он не может быть изменен. Если бы
i
было Integer, тоi++
было бы создать новый объект Integer - гораздо дороже с точки зрения памяти и процессора.источник
i++
для другой переменной, поэтому Integer вполне должен быть неизменным, чтобы иметь возможность сделать это (или, по крайней мере, этоi++
должно было бы создать новый объект Integer в любом случае). (И примитивные значения тоже неизменны - вы просто не замечаете этого, поскольку они не являются объектами.)++
красная сельдь здесь. Представьте себе , Java была расширена для поддержки оператора перегрузки в очень простым способом, таким образом, что если класс (например,Integer
есть методplus
, то вы можете написатьi + 1
вместоi.plus(1)
. И предположим также , что компилятор достаточно умен , чтобы расширитьi++
вi = i + 1
. Теперь можно сказать ,i++
и эффективно «приращение переменной я» , неInteger
будучи изменяемым.Прежде всего, привычка. Если вы программировали на Java в течение восьми лет, вы накапливаете значительное количество инерции. Зачем менять, если нет веских причин для этого? Это не значит, что использование коробочных примитивов дает дополнительные преимущества.
Другая причина состоит в том, чтобы утверждать, что
null
это недопустимый вариант. Было бы бессмысленно и неправильно вводить сумму двух чисел или переменную цикла какInteger
.Это тоже аспект производительности, в то время как во многих случаях разница в производительности не критична (хотя когда это так, это довольно плохо), никто не любит писать код, который можно было бы написать так же легко, быстрее, чем мы уже привык к.
источник
Кстати, Smalltalk имеет только объекты (без примитивов), и все же они оптимизировали свои маленькие целые числа (используя не все 32 бита, только 27 или около того), чтобы не выделять пространство кучи, а просто использовать специальный битовый шаблон. Также другие общие объекты (true, false, null) имели здесь специальные битовые комбинации.
Таким образом, по крайней мере на 64-битных JVM (с пространством имен указателей на 64 бита) должна быть возможность вообще не иметь никаких объектов Integer, Character, Byte, Short, Boolean, Float (и small Long) (кроме созданных ими). явным образом
new ...()
), только специальные битовые комбинации, которыми могут эффективно управлять обычные операторы.источник
Я не могу поверить, что никто не упомянул, что я думаю, что это самая важная причина: «int» так гораздо проще набрать, чем «Integer». Я думаю, что люди недооценивают важность краткого синтаксиса. Производительность на самом деле не является причиной для того, чтобы их избегать, потому что большую часть времени, когда кто-либо использует числа, находится в индексах цикла, а увеличение и сравнение этих значений ничего не стоит в любом нетривиальном цикле (независимо от того, используете ли вы int или Integer).
Другая причина заключалась в том, что вы можете получить NPE, но этого очень просто избежать с помощью коробочных типов (и этого гарантированно избежать, если вы всегда инициализируете их ненулевыми значениями).
Другая причина заключалась в том, что (new Long (1000)) == (new Long (1000)) имеет значение false, но это просто еще один способ сказать, что «.equals» не имеет синтаксической поддержки коробочных типов (в отличие от операторов <,> , = и т. д.), поэтому мы возвращаемся к причине «более простого синтаксиса».
Я думаю, что пример непримитивного цикла Стива Йегге очень хорошо иллюстрирует мою точку зрения: http://sites.google.com/site/steveyegge2/language-trickery-and-ejb
Подумайте об этом: как часто вы используете типы функций в языках, которые имеют хороший синтаксис для них (например, любой функциональный язык, python, ruby и даже C) по сравнению с Java, где вы должны имитировать их с помощью интерфейсов, таких как Runnable и Callable, и безымянные занятия.
источник
Пара причин, чтобы не избавляться от примитивов:
Если это будет устранено, любые старые программы даже не запустятся.
Вся JVM должна быть переписана для поддержки этой новой вещи.
Вам нужно будет сохранить значение и ссылку, которая использует больше памяти. Если у вас огромный массив байтов, использование
byte
's значительно меньше, чем использованиеByte
'.Объявление, а
int i
затем выполнениеi
чего-либо, не вызовет никаких проблем, но объявление,Integer i
а затем выполнение того же действия приведет к NPE.Рассмотрим этот код:
Было бы ложным. Операторы должны быть перегружены, и это приведет к серьезной переписке материала.
Обертки объектов значительно медленнее, чем их примитивные аналоги.
источник
Объекты гораздо более тяжелые, чем примитивные типы, поэтому примитивные типы гораздо более эффективны, чем экземпляры классов-оболочек.
Примитивные типы очень просты: например, int 32-битный и занимает ровно 32 бита в памяти, и им можно манипулировать напрямую. Целочисленный объект - это законченный объект, который (как и любой объект) должен храниться в куче, и доступ к нему возможен только через ссылку (указатель) на него. Скорее всего, он также занимает более 32 бит (4 байта) памяти.
Тем не менее, тот факт, что в Java есть различие между примитивными и не примитивными типами, также является признаком возраста языка программирования Java. Более новые языки программирования не имеют этого различия; компилятор такого языка достаточно умен, чтобы самому определить, используете ли вы простые значения или более сложные объекты.
Например, в Scala нет примитивных типов; есть класс Int для целых чисел, а Int - это реальный объект (который вы можете использовать для методов и т. д.). Когда компилятор компилирует ваш код, он использует примитивные int за кулисами, поэтому использование Int столь же эффективно, как и использование примитива int в Java.
источник
В дополнение к тому, что сказали другие, примитивные локальные переменные выделяются не из кучи, а вместо этого в стеке. Но объекты выделяются из кучи и, следовательно, должны быть удалены.
источник
Примитивные типы имеют много преимуществ:
источник
Трудно понять, какие оптимизации происходят под прикрытием.
Для локального использования, когда компилятор имеет достаточно информации для оптимизации, исключая возможность нулевого значения, я ожидаю, что производительность будет такой же или похожей .
Тем не менее, массивы примитивов, очевидно, сильно отличаются от коллекций в штучной упаковке примитивов. Это имеет смысл, учитывая, что в глубине коллекции возможно очень мало оптимизаций.
Более того, логические издержки
Integer
намного выше по сравнению с : теперь вам нужно беспокоиться о том, генерирует ли исключение или нет .int
int a = b + c;
Я бы максимально использовал примитивы и полагался на фабричные методы и автобокс, чтобы дать мне более семантически мощные коробочные типы, когда они необходимы.
источник
С другой стороны, я бы не отказался увидеть, как что-то подобное находит путь в Java.
Где цикл for автоматически увеличивает loop1 от 0 до 1000 или
Где цикл for автоматически уменьшает loop1 1000 до 0.
источник
Вы должны спросить, почему требуется класс / тип объекта
Причина наличия Типа объекта состоит в том, чтобы облегчить нашу жизнь, когда мы имеем дело с Коллекциями. Примитивы не могут быть добавлены непосредственно в List / Map, вам нужно написать класс-оболочку. Здесь вам помогут готовые классы целочисленных типов, а также множество полезных методов, таких как Integer.pareseInt (str).
источник
Я согласен с предыдущими ответами, используя примитивы обертки объектов может быть дорого. Но если производительность не критична для вашего приложения, вы избегаете переполнения при использовании объектов. Например:
Значение
bigNumber
-2147483647, и вы ожидаете, что оно будет 2147483649. Это ошибка в коде, которую можно исправить, выполнив:И
bigNumber
было бы 2147483649. Такие ошибки иногда легко пропустить и могут привести к неизвестному поведению или уязвимостям (см. CWE-190 ).Если вы используете объекты-оболочки, эквивалентный код не будет компилироваться.
Так что проще решить подобные проблемы, используя объекты-обертки примитивов.
На ваш вопрос уже ответили так, что я отвечаю просто для того, чтобы добавить немного больше информации, не упомянутой ранее.
источник
Потому что JAVA выполняет все математические операции в примитивных типах. Рассмотрим этот пример:
Здесь операции напоминания и унарного плюса не могут применяться к типу Integer (Reference), компилятор выполняет распаковку и выполняет операции.
Итак, убедитесь, сколько операций автобоксирования и распаковки происходит в Java-программе. Поскольку для выполнения этой операции требуется время.
Как правило, лучше сохранять аргументы типа Reference и результат примитивного типа.
источник
Эти примитивные типы являются гораздо быстрее и требуют гораздо меньше памяти . Поэтому мы можем захотеть использовать их.
С другой стороны, текущая спецификация языка Java не позволяет использовать примитивные типы в параметризованных типах (обобщениях), в коллекциях Java или API Reflection.
Когда нашему приложению нужны коллекции с большим количеством элементов, мы должны рассмотреть возможность использования массивов с как можно более «экономичным» типом.
* Для получения подробной информации см. Источник: https://www.baeldung.com/java-primitives-vs-objects
источник
Чтобы быть кратким: примитивные типы быстрее и требуют меньше памяти, чем в штучной упаковке
источник