Почему люди все еще используют примитивные типы в Java?
начиная с Java 5, у нас был бокс/распаковка примитивных типов, так что int завернут, чтобы быть java.lang.Integer, и так далее и тому подобное.
Я вижу много новых проектов Java в последнее время (то наверняка требуется JRE по крайней мере версии 5, если не 6), которые используют int, а не java.lang.Integer, хотя его намного удобнее использовать последний, так как он имеет несколько вспомогательных методов для преобразования long значения и др.
почему некоторые еще использовать примитивные типы в Java? Есть ли какая-то ощутимая польза?
19 ответов:
у Джошуа Блоха Эффективная Java, пункт 5: "Избегайте создания ненужных объектов", он публикует следующий пример кода:
public static void main(String[] args) { Long sum = 0L; // uses Long, not long for (long i = 0; i <= Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); }и это занимает 43 секунды, чтобы запустить. Принимая долго в примитиве приносит его до 6,8 секунд... Если это указывает на то, почему мы используем примитивы.
отсутствие собственного равенства ценностей также вызывает озабоченность (
.equals()довольно многословно по сравнению с==)для biziclop:
class Biziclop { public static void main(String[] args) { System.out.println(new Integer(5) == new Integer(5)); System.out.println(new Integer(500) == new Integer(500)); System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); System.out.println(Integer.valueOf(500) == Integer.valueOf(500)); } }результаты:
false false true falseEDITпочему (3) Возвращение
trueи (4) возвратитьfalse?потому что это два разных объекта. 256 целых чисел, ближайших к нулю [-128; 127], кэшируются JVM, поэтому они возвращают один и тот же объект для них. Однако за пределами этого диапазона они не кэшируются, поэтому создается новый объект. Чтобы сделать вещи более сложными, JLS требует, чтобы at по крайней мере 256 flyweights кэшируются. Разработчики JVM могут добавить больше, если захотят, что означает, что это может работать в системе, где ближайшие 1024 кэшируются, и все они возвращают true... # неловко
Autounboxing может привести к трудно определить NPEs
Integer in = null; ... ... int i = in; // NPE at runtimeв большинстве случаев нулевое присвоение
inнамного менее очевидно, чем выше.
примитивные типы:
int x = 1000; int y = 1000;теперь оценивать:
x == yэто
true. Ничего удивительного. Теперь попробуйте коробочные типы:Integer x = 1000; Integer y = 1000;теперь оценивать:
x == yэто
false. Возможно. Зависит от времени выполнения. Это достаточная причина?
помимо проблем с производительностью и памятью, я хотел бы придумать другой вопрос:
Listинтерфейс был бы сломан безint.
Проблема в перегруженностиremove()метод (remove(int)иremove(Object)).remove(Integer)всегда будет принято решение о вызове последнего, поэтому вы не можете удалить элемент по индексу.С другой стороны, есть ловушка при попытке добавить и удалить
int:final int i = 42; final List<Integer> list = new ArrayList<Integer>(); list.add(i); // add(Object) list.remove(i); // remove(int) - Ouch!
вы действительно можете себе представить
for (int i=0; i<10000; i++) { do something }цикл с java.ленг.Вместо целого числа? Ява.ленг.Integer является неизменяемым, поэтому каждое приращение вокруг цикла создаст новый объект java в куче, а не просто увеличит int в стеке с помощью одной инструкции JVM. Представление было бы дьявольским.
Я бы действительно не согласился, что это очень удобный режим для использования java.ленг.Целое число, чем int. Напротив того. Автоупаковка означает, что вы можете использовать int, где вы в противном случае пришлось бы использовать Integer, и компилятор java позаботится о вставке кода для создания нового объекта Integer для вас. Autoboxing - это все, что позволяет вам использовать int, где ожидается целое число, с компилятором, вставляющим соответствующую конструкцию объекта. Это никоим образом не удаляет или уменьшает потребность в int в первую очередь. С autoboxing вы получаете самое лучшее обоих миров. Вы получаете целое число, созданное для вас автоматически, когда вам нужен объект Java на основе кучи, и вы получаете скорость и эффективность int, когда вы просто делаете арифметические и локальные вычисления.
примитивные типы много быстрее:
int i; i++;целое число (все числа, а также строка) является неизменяемые тип: после создания он не может быть изменен. Если
iбыло целое число, чемi++создаст новый целочисленный объект-гораздо дороже с точки зрения памяти и процессора.
прежде всего, привычка. Если вы кодируете на Java в течение восьми лет, вы накапливаете значительное количество инерции. Зачем меняться, если для этого нет веских причин? Это не так, как если бы использование коробочных примитивов имело какие-либо дополнительные преимущества.
другая причина заключается в том, чтобы утверждать, что
nullне является допустимым вариантом. Было бы бессмысленно и ошибочно объявлять сумму двух чисел или переменной цикла какInteger.есть аспект производительности тоже, хотя разница в производительности не критична во многих случаях (хотя, когда это так, это довольно плохо), никто не любит писать код, который можно было бы написать так же легко и быстрее, как мы уже привыкли.
кстати, Smalltalk имеет только объекты (без примитивов), и все же они оптимизировали свои маленькие целые числа (используя не все 32 бита, только 27 или такие), чтобы не выделять пространство кучи, а просто использовать специальный битовый шаблон. Также другие общие объекты (true, false, null) имели здесь специальные битовые шаблоны.
таким образом, по крайней мере на 64-битных JVMs (с 64-битным пространством имен указателей) должно быть возможно не иметь никаких объектов Integer, Character, Byte, Short, Boolean, Float (и small Long) at все (кроме этих созданных явным
new ...()), только специальные битовые шаблоны, которыми вполне эффективно могут манипулировать обычные операторы.
Я не могу поверить, что никто не упомянул, что я считаю наиболее важной: "int" - это так, гораздо проще ввести, чем"Integer". Я думаю, что люди недооценивают важность синтаксиса. Производительность на самом деле не является причиной избегать их, потому что большую часть времени, когда вы используете числа, находится в индексах цикла, а увеличение и сравнение этих затрат ничего не стоит в любом нетривиальном цикле (независимо от того, используете ли вы int или Integer).
другой причиной было то, что вы можете получить NPEs, но это очень легко избежать с коробочными типами (и это гарантированно избежать, если вы всегда инициализируете их в ненулевые значения).
другая причина заключалась в том, что(new Long(1000))==(new Long (1000)) является ложным, но это просто другой способ сказать это ".equals " не имеет синтаксической поддержки для коробочных типов (в отличие от операторов, = и т. д.), Поэтому мы возвращаемся к причине "более простого синтаксиса".
Я думаю, что пример непримитивного цикла Стива Егге очень хорошо иллюстрирует мою точку зрения : http://sites.google.com/site/steveyegge2/language-trickery-and-ejb
подумайте об этом: как часто вы используете типы функций в языках, которые имеют хороший синтаксис для них (например, любой функциональный язык, python, ruby и даже C) по сравнению с java, где вы должны имитировать их с помощью интерфейсов, таких как запускаемые и вызываемые и безымянные классы.
пара причин не избавляться от примитивов:
- назад compatability.
если он будет устранен, любые старые программы даже не будут работать.
- JVM переписать.
весь JVM должен быть переписан, чтобы поддержать эту новую вещь.
- больший объем памяти.
вам нужно будет сохранить значение и ссылку, которая использует больше памяти. Если у вас есть огромный массив байтов, используя
byte' s значительно меньше, чем при использованииByte' s.
- проблемы с нулевым указателем.
объявления
int iзатем делать вещи сiне приведет ни к каким проблемам, но объявлениеInteger iи затем делать то же самое приведет к NPE.
- вопросы равенства.
рассмотрим этот код:
Integer i1 = 5; Integer i2 = 5; i1 == i2; // Currently would be false.было бы ложно. Операторы должны быть перегружены, и это приведет в крупном переписывании материала.
- медленно
обертки объектов значительно медленнее, чем их примитивные аналоги.
в дополнение к тому, что говорили другие, примитивные локальные переменные не выделяются из кучи, а вместо этого в стеке. Но объекты выделяются из кучи и поэтому должны быть собраны в мусор.
объекты намного тяжелее, чем примитивные типы, поэтому примитивные типы намного эффективнее, чем экземпляры классов-оболочек.
примитивные типы очень просты: например, int составляет 32 бита и занимает ровно 32 бита в памяти, и может управляться напрямую. Целочисленный объект-это полный объект, который (как и любой объект) должен храниться в куче и может быть доступен только через ссылку (указатель) на него. Это, скорее всего, также занимает более 32 бит (4 байта) памяти.
тем не менее, тот факт, что Java имеет различие между примитивными и непримитивными типами, также является признаком возраста языка программирования Java. Более новые языки программирования не имеют такого различия; компилятор такого языка достаточно умен, чтобы понять, используете ли вы простые значения или более сложные объекты.
например, в Scala нет примитивных типов; есть класс Int для целых чисел, а Int-реальный объект (что вы можете методы и т. д.). Когда компилятор компилирует ваш код, он использует примитивные int за кулисами, поэтому использование Int так же эффективно, как использование примитивного int в Java.
трудно понять, какие оптимизации происходят под крышками.
для локального использования, когда компилятор имеет достаточно информации для оптимизации, исключающей возможность нулевого значения,Я ожидаю, что производительность будет такой же или аналогичной.
однако массивы примитивов, по-видимому,очень разные из коллекции упакованных примитивов. Это имеет смысл, учитывая, что очень немногие оптимизации можно глубже в пределах коллекции.
кроме того,
Integerгораздо выше логические накладные по сравнению сint: теперь вы должны беспокоиться о том, является ли или нетint a = b + c;выдает исключение.Я бы использовал примитивы как можно больше и полагался на заводские методы и автобоксинг, чтобы дать мне более семантически мощные коробочные типы, когда они нужны.
int loops = 100000000; long start = System.currentTimeMillis(); for (Long l = new Long(0); l<loops;l++) { //System.out.println("Long: "+l); } System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start)); start = System.currentTimeMillis(); for (long l = 0; l<loops;l++) { //System.out.println("long: "+l); } System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));миллисекунды, затраченные на цикл "100000000" раз вокруг долго: 468
миллисекунды, затраченные на цикл "100000000" раз вокруг долго: 31
на стороне примечание, Я бы не прочь увидеть что-то вроде этого найти это путь в Java.
Integer loop1 = new Integer(0); for (loop1.lessThan(1000)) { ... }где цикл for автоматически увеличивает loop1 от 0 до 1000 или
Integer loop1 = new Integer(1000); for (loop1.greaterThan(0)) { ... }где цикл for автоматически уменьшает loop1 1000 до 0.
примитивные типы имеют много преимуществ:
- более простой код для записи
- производительность лучше, так как вы не создаете экземпляр объекта для переменной
- , поскольку они не представляют собой ссылку на объект нет необходимости проверять на null
- используйте примитивные типы, если вам не нужно воспользоваться функциями бокса.
Я согласен с предыдущими ответами, используя примитивы объекты обертки могут быть дорогими. Но, если производительность не критична в вашем приложении, вы избегаете переполнения при использовании объектов. Например:
long bigNumber = Integer.MAX_VALUE + 2;значение
bigNumberis -2147483647, и вы ожидаете, что это будет 2147483649. Это ошибка в коде, которая будет исправлена путем выполнения:long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now.и
bigNumberбудет 2147483649. Такие ошибки иногда легко пропустить и может привести к неизвестным поведение или уязвимости (см. CWE-190).если вы используете объекты-оболочки, эквивалентный код не будет компилироваться.
Long bigNumber = Integer.MAX_VALUE + 2; // Not compilingтаким образом, проще остановить такие проблемы с помощью объектов-оболочек примитивов.
на ваш вопрос уже ответили так, что я отвечаю просто, чтобы добавить немного больше информации, не упомянутой ранее.
- вам нужны примитивы для выполнения математических операций
- примитивы занимает меньше памяти, как ответили выше и лучше выполнит
вы должны спросить, почему класс / тип объекта требуется
причина наличия типа объекта состоит в том, чтобы облегчить нашу жизнь, когда мы имеем дело с коллекциями. Примитивы не могут быть добавлены непосредственно в список/карта, а вам нужно написать класс-оболочку. Готовый целочисленный вид классов помогает вам здесь плюс у него есть много служебные методы, такие как Integer.pareseInt (str)
потому что JAVA выполняет все математические операции в примитивных типах. Рассмотрим следующий пример:
public static int sumEven(List<Integer> li) { int sum = 0; for (Integer i: li) if (i % 2 == 0) sum += i; return sum; }здесь операции reminder и unary plus не могут быть применены к целочисленному (ссылочному) типу, компилятор выполняет распаковку и выполняет операции.
Итак, убедитесь, сколько операций автобокса и распаковки происходит в программе java. Так как для выполнения этой операции требуется время.
Как правило, лучше сохранить аргументы типа Reference и результат примитивного типа.
Comments